diff options
Diffstat (limited to 't/app/controller')
31 files changed, 5763 insertions, 1778 deletions
diff --git a/t/app/controller/about.t b/t/app/controller/about.t index 4e49cdac9..cec50abfa 100644 --- a/t/app/controller/about.t +++ b/t/app/controller/about.t @@ -1,3 +1,4 @@ +use utf8; use strict; use warnings; @@ -7,25 +8,26 @@ use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App'; ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' ); # check that we can get the page -$mech->get_ok('/about'); -$mech->content_like(qr{About us ::\s+FixMyStreet}); +$mech->get_ok('/faq'); +$mech->content_like(qr{Frequently Asked Questions ::\s+FixMyStreet}); $mech->content_contains('html class="no-js" lang="en-gb"'); -SKIP: { - skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 8 ) - unless FixMyStreet::Cobrand->exists('emptyhomes'); +$mech->get_ok('/privacy'); +is $mech->res->code, 200, "got 200 for final destination"; +is $mech->res->previous->code, 302, "got 302 for redirect"; +is $mech->uri->path, '/about/privacy'; - # check that geting the page as EHA produces a different page - ok $mech->host("reportemptyhomes.co.uk"), 'change host to reportemptyhomes'; - $mech->get_ok('/about'); - $mech->content_like(qr{About us ::\s+Report Empty Homes}); - $mech->content_contains('html lang="en-gb"'); +$mech->get('/about/page-that-does-not-exist'); +ok !$mech->res->is_success(), "want a bad response"; +is $mech->res->code, 404, "got 404"; - # check that geting the page as EHA in welsh produces a different page - ok $mech->host("cy.reportemptyhomes.co.uk"), 'host to cy.reportemptyhomes'; - $mech->get_ok('/about'); - $mech->content_like(qr{Amdanom ni ::\s+Adrodd am Eiddo Gwag}); - $mech->content_contains('html lang="cy"'); -} +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fiksgatami' ], +}, sub { + ok $mech->host("www.fiksgatami.no"), 'host to fiksgatami'; + $mech->get_ok('/faq'); + $mech->content_like(qr{Ofte spurte spørsmål ::}); + $mech->content_contains('html class="no-js" lang="nb"'); +}; done_testing(); diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t index 71a391c59..5f8abe5a6 100644 --- a/t/app/controller/admin.t +++ b/t/app/controller/admin.t @@ -6,28 +6,23 @@ use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; -my $secret = FixMyStreet::App->model('DB::Secret')->search(); +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); -# don't explode if there's nothing in the secret table -if ( $secret == 0 ) { - diag "You need to put an entry in the secret table for the admin tests to run"; - plan skip_all => 'No entry in secret table'; -} +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); -my $user = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'test@example.com', name => 'Test User' } ); -ok $user, "created test user"; +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council', id => 2237); +my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); +$mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Traffic lights', email => 'lights@example.com' ); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); -my $user2 = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'test2@example.com', name => 'Test User 2' } ); -ok $user2, "created second test user"; +my $oxford = $mech->create_body_ok(2421, 'Oxford City Council'); +$mech->create_contact_ok( body_id => $oxford->id, category => 'Graffiti', email => 'graffiti@example.net' ); +my $bromley = $mech->create_body_ok(2482, 'Bromley Council', id => 2482); -my $user3 = - FixMyStreet::App->model('DB::User') - ->find( { email => 'test3@example.com', name => 'Test User 2' } ); +my $user3 = $mech->create_user_ok('test3@example.com', name => 'Test User 2'); if ( $user3 ) { $mech->delete_user( $user3 ); @@ -45,7 +40,7 @@ my $dt = DateTime->new( my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { postcode => 'SW1A 1AA', - council => '2504', + bodies_str => '2504', areas => ',105255,11806,11828,2247,2504,', category => 'Other', title => 'Report to Edit', @@ -53,6 +48,7 @@ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( used_map => 't', name => 'Test User', anonymous => 'f', + external_id => '13', state => 'confirmed', confirmed => $dt->ymd . ' ' . $dt->hms, lang => 'en-gb', @@ -69,13 +65,15 @@ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( my $alert = FixMyStreet::App->model('DB::Alert')->find_or_create( { - alert_type => 'new_updates', - parameter => $report->id, + alert_type => 'area_problems', + parameter => 2482, confirmed => 1, user => $user, }, ); +$mech->log_in_ok( $superuser->email ); + subtest 'check summary counts' => sub { my $problems = FixMyStreet::App->model('DB::Problem')->search( { state => { -in => [qw/confirmed fixed closed investigating planned/, 'in progress', 'fixed - user', 'fixed - council'] } } ); @@ -84,16 +82,20 @@ subtest 'check summary counts' => sub { my $problem_count = $problems->count; $problems->update( { cobrand => '' } ); - FixMyStreet::App->model('DB::Problem')->search( { council => 2489 } )->update( { council => 1 } ); + FixMyStreet::App->model('DB::Problem')->search( { bodies_str => 2489 } )->update( { bodies_str => 1 } ); my $q = FixMyStreet::App->model('DB::Questionnaire')->find_or_new( { problem => $report, }); - $q->whensent( \'ms_current_timestamp()' ); + $q->whensent( \'current_timestamp' ); $q->in_storage ? $q->update : $q->insert; my $alerts = FixMyStreet::App->model('DB::Alert')->search( { confirmed => { '>' => 0 } } ); my $a_count = $alerts->count; - $mech->get_ok('/admin'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + }, sub { + $mech->get_ok('/admin'); + }; $mech->title_like(qr/Summary/); @@ -105,11 +107,10 @@ subtest 'check summary counts' => sub { $mech->content_contains( "$q_count questionnaires sent" ); - SKIP: { - skip( "Need 'barnet' in ALLOWED_COBRANDS config", 7 ) - unless FixMyStreet::Cobrand->exists('barnet'); - - ok $mech->host('barnet.fixmystreet.com'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + ok $mech->host('oxfordshire.fixmystreet.com'); $mech->get_ok('/admin'); $mech->title_like(qr/Summary/); @@ -118,11 +119,11 @@ subtest 'check summary counts' => sub { my ($num_alerts) = $mech->content =~ /(\d+) confirmed alerts/; my ($num_qs) = $mech->content =~ /(\d+) questionnaires sent/; - $report->council(2489); - $report->cobrand('barnet'); + $report->bodies_str(2237); + $report->cobrand('oxfordshire'); $report->update; - $alert->cobrand('barnet'); + $alert->cobrand('oxfordshire'); $alert->update; $mech->get_ok('/admin'); @@ -131,48 +132,56 @@ subtest 'check summary counts' => sub { $mech->content_contains( ($num_alerts+1) . " confirmed alerts" ); $mech->content_contains( ($num_qs+1) . " questionnaires sent" ); - $report->council(2504); + $report->bodies_str(2504); $report->cobrand(''); $report->update; $alert->cobrand(''); $alert->update; - } + }; - FixMyStreet::App->model('DB::Problem')->search( { council => 1 } )->update( { council => 2489 } ); - ok $mech->host('fixmystreet.com'); + FixMyStreet::App->model('DB::Problem')->search( { bodies_str => 1 } )->update( { bodies_str => 2489 } ); + ok $mech->host('www.fixmystreet.com'); }; -my $host = FixMyStreet->config('BASE_URL'); -$mech->get_ok('/admin/council_contacts/2650'); +# This override is wrapped around ALL the /admin/body tests +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + MAPIT_TYPES => [ 'UTA' ], + BASE_URL => 'http://www.example.org', +}, sub { + +my $body = $mech->create_body_ok(2650, 'Aberdeen City Council'); +$mech->get_ok('/admin/body/' . $body->id); $mech->content_contains('Aberdeen City Council'); $mech->content_like(qr{AB\d\d}); -$mech->content_contains("$host/around"); +$mech->content_contains("http://www.example.org/around"); subtest 'check contact creation' => sub { my $contact = FixMyStreet::App->model('DB::Contact')->search( - { body_id => 2650, category => [ 'test category', 'test/category' ] } + { body_id => $body->id, category => [ 'test category', 'test/category' ] } ); $contact->delete_all; my $history = FixMyStreet::App->model('DB::ContactsHistory')->search( - { body_id => 2650, category => [ 'test category', 'test/category' ] } + { body_id => $body->id, category => [ 'test category', 'test/category' ] } ); $history->delete_all; - $mech->get_ok('/admin/council_contacts/2650'); + $mech->get_ok('/admin/body/' . $body->id); $mech->submit_form_ok( { with_fields => { category => 'test category', email => 'test@example.com', note => 'test note', non_public => undef, + confirmed => 0, } } ); $mech->content_contains( 'test category' ); - $mech->content_contains( '<td>test@example.com' ); + $mech->content_contains( 'test@example.com' ); $mech->content_contains( '<td>test note' ); - $mech->content_contains( '<td>Public' ); + $mech->content_contains( 'Private: No' ); $mech->submit_form_ok( { with_fields => { category => 'private category', @@ -182,7 +191,7 @@ subtest 'check contact creation' => sub { } } ); $mech->content_contains( 'private category' ); - $mech->content_contains( '<td>Non Public' ); + $mech->content_contains( 'Private: Yes' ); $mech->submit_form_ok( { with_fields => { category => 'test/category', @@ -190,57 +199,67 @@ subtest 'check contact creation' => sub { note => 'test/note', non_public => 'on', } } ); - $mech->get_ok('/admin/council_edit/2650/test/category'); + $mech->get_ok('/admin/body/' . $body->id . '/test/category'); }; subtest 'check contact editing' => sub { - $mech->get_ok('/admin/council_edit/2650/test%20category'); + $mech->get_ok('/admin/body/' . $body->id .'/test%20category'); - $mech->submit_form_ok( { with_fields => { + $mech->submit_form_ok( { with_fields => { email => 'test2@example.com', note => 'test2 note', non_public => undef, } } ); $mech->content_contains( 'test category' ); - $mech->content_contains( '<td>test2@example.com' ); + $mech->content_contains( 'test2@example.com' ); $mech->content_contains( '<td>test2 note' ); - $mech->content_contains( '<td>Public' ); + $mech->content_contains( 'Private: No' ); - $mech->submit_form_ok( { with_fields => { + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->submit_form_ok( { with_fields => { + email => 'test2@example.com, test3@example.com', + note => 'test3 note', + } } ); + + $mech->content_contains( 'test2@example.com,test3@example.com' ); + + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->content_contains( '<td><strong>test2@example.com,test3@example.com' ); + + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->submit_form_ok( { with_fields => { email => 'test2@example.com', note => 'test2 note', non_public => 'on', } } ); - $mech->content_contains( '<td>Non Public' ); + $mech->content_contains( 'Private: Yes' ); - $mech->get_ok('/admin/council_edit/2650/test%20category'); + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); $mech->content_contains( '<td><strong>test2@example.com' ); }; subtest 'check contact updating' => sub { - $mech->get_ok('/admin/council_edit/2650/test%20category'); + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); $mech->content_like(qr{test2\@example.com</strong>[^<]*</td>[^<]*<td>No}s); - $mech->get_ok('/admin/council_contacts/2650'); + $mech->get_ok('/admin/body/' . $body->id); $mech->form_number( 1 ); $mech->tick( 'confirmed', 'test category' ); $mech->submit_form_ok({form_number => 1}); - $mech->content_like(qr'test2@example.com</td>[^<]*<td>Yes's); - $mech->get_ok('/admin/council_edit/2650/test%20category'); + $mech->content_like(qr'test2@example.com</td>[^<]*<td>\s*Confirmed: Yes's); + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); $mech->content_like(qr{test2\@example.com[^<]*</td>[^<]*<td><strong>Yes}s); }; -my $open311 = - FixMyStreet::App->model('DB::Body')->search( { area_id => 2650 } ); -$open311->delete if $open311; +$body->update({ send_method => undef }); subtest 'check open311 configuring' => sub { - $mech->get_ok('/admin/council_contacts/2650/'); + $mech->get_ok('/admin/body/' . $body->id); $mech->content_lacks('Council contacts configured via Open311'); $mech->form_number(3); @@ -256,13 +275,9 @@ subtest 'check open311 configuring' => sub { } ); $mech->content_contains('Council contacts configured via Open311'); - $mech->content_contains('Configuration updated - contacts will be generated automatically later'); - - $open311 = - FixMyStreet::App->model('DB::Body')->search( { area_id => 2650 } ); + $mech->content_contains('Values updated'); - is $open311->count, 1, 'only one configuration'; - my $conf = $open311->first; + my $conf = FixMyStreet::App->model('DB::Body')->find( $body->id ); is $conf->endpoint, 'http://example.com/open311', 'endpoint configured'; is $conf->api_key, 'api key', 'api key configured'; is $conf->jurisdiction, 'mySociety', 'jurisdiction configures'; @@ -280,24 +295,24 @@ subtest 'check open311 configuring' => sub { } ); - $mech->content_contains('Configuration updated'); + $mech->content_contains('Values updated'); - $open311 = - FixMyStreet::App->model('DB::Body')->search( { area_id => 2650 } ); - - is $open311->count, 1, 'only one configuration'; - $conf = $open311->first; + $conf = FixMyStreet::App->model('DB::Body')->find( $body->id ); is $conf->endpoint, 'http://example.org/open311', 'endpoint updated'; is $conf->api_key, 'new api key', 'api key updated'; is $conf->jurisdiction, 'open311', 'jurisdiction configures'; }; subtest 'check text output' => sub { - $mech->get_ok('/admin/council_contacts/2650?text=1'); + $mech->get_ok('/admin/body/' . $body->id . '?text=1'); is $mech->content_type, 'text/plain'; $mech->content_contains('test category'); }; + +}; # END of override wrap + + my $log_entries = FixMyStreet::App->model('DB::AdminLog')->search( { object_type => 'problem', @@ -327,7 +342,6 @@ foreach my $test ( non_public => undef, }, changes => { title => 'Edited Report', }, - log_count => 1, log_entries => [qw/edit/], resend => 0, }, @@ -344,7 +358,6 @@ foreach my $test ( non_public => undef, }, changes => { detail => 'Edited Detail', }, - log_count => 2, log_entries => [qw/edit edit/], resend => 0, }, @@ -361,7 +374,6 @@ foreach my $test ( non_public => undef, }, changes => { name => 'Edited User', }, - log_count => 3, log_entries => [qw/edit edit edit/], resend => 0, user => $user, @@ -381,7 +393,6 @@ foreach my $test ( changes => { flagged => 'on', }, - log_count => 4, log_entries => [qw/edit edit edit edit/], resend => 0, user => $user, @@ -399,7 +410,6 @@ foreach my $test ( non_public => undef, }, changes => { email => $user2->email, }, - log_count => 5, log_entries => [qw/edit edit edit edit edit/], resend => 0, user => $user2, @@ -417,8 +427,7 @@ foreach my $test ( non_public => undef, }, changes => { state => 'unconfirmed' }, - log_count => 6, - log_entries => [qw/state_change edit edit edit edit edit/], + log_entries => [qw/edit state_change edit edit edit edit edit/], resend => 0, }, { @@ -434,8 +443,7 @@ foreach my $test ( non_public => undef, }, changes => { state => 'confirmed' }, - log_count => 7, - log_entries => [qw/state_change state_change edit edit edit edit edit/], + log_entries => [qw/edit state_change edit state_change edit edit edit edit edit/], resend => 0, }, { @@ -451,9 +459,8 @@ foreach my $test ( non_public => undef, }, changes => { state => 'fixed' }, - log_count => 8, log_entries => - [qw/state_change state_change state_change edit edit edit edit edit/], + [qw/edit state_change edit state_change edit state_change edit edit edit edit edit/], resend => 0, }, { @@ -469,9 +476,8 @@ foreach my $test ( non_public => undef, }, changes => { state => 'hidden' }, - log_count => 9, log_entries => [ - qw/state_change state_change state_change state_change edit edit edit edit edit/ + qw/edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ ], resend => 0, }, @@ -491,9 +497,8 @@ foreach my $test ( state => 'confirmed', anonymous => 1, }, - log_count => 11, log_entries => [ - qw/edit state_change state_change state_change state_change state_change edit edit edit edit edit/ + qw/edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ ], resend => 0, }, @@ -510,9 +515,8 @@ foreach my $test ( non_public => undef, }, changes => {}, - log_count => 12, log_entries => [ - qw/resend edit state_change state_change state_change state_change state_change edit edit edit edit edit/ + qw/resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ ], resend => 1, }, @@ -531,9 +535,8 @@ foreach my $test ( changes => { non_public => 'on', }, - log_count => 13, log_entries => [ - qw/edit resend edit state_change state_change state_change state_change state_change edit edit edit edit edit/ + qw/edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ ], resend => 0, }, @@ -543,6 +546,7 @@ foreach my $test ( $log_entries->reset; $mech->get_ok("/admin/report_edit/$report_id"); + @{$test->{fields}}{'external_id', 'external_body', 'external_team', 'category'} = (13, "", "", "Other"); is_deeply( $mech->visible_form_values(), $test->{fields}, 'initial form values' ); my $new_fields = { @@ -557,13 +561,13 @@ foreach my $test ( } is_deeply( $mech->visible_form_values(), $new_fields, 'changed form values' ); - is $log_entries->count, $test->{log_count}, 'log entry count'; + is $log_entries->count, scalar @{$test->{log_entries}}, 'log entry count'; is $log_entries->next->action, $_, 'log entry added' for @{ $test->{log_entries} }; $report->discard_changes; - if ( $report->state eq 'confirmed' ) { - $mech->content_contains( 'type="submit" name="resend"', 'no resend button' ); + if ($report->state eq 'confirmed' && $report->whensent) { + $mech->content_contains( 'type="submit" name="resend"', 'resend button' ); } else { $mech->content_lacks( 'type="submit" name="resend"', 'no resend button' ); } @@ -584,6 +588,33 @@ foreach my $test ( }; } +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'fixmystreet', +}, sub { + +subtest 'change report category' => sub { + my ($ox_report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Unsure', { + category => 'Potholes', + areas => ',2237,2421,', # Cached used by categories_for_point... + latitude => 51.7549262252, + longitude => -1.25617899435, + whensent => \'current_timestamp', + }); + $mech->get_ok("/admin/report_edit/" . $ox_report->id); + + $mech->submit_form_ok( { with_fields => { category => 'Traffic lights' } }, 'form_submitted' ); + $ox_report->discard_changes; + is $ox_report->category, 'Traffic lights'; + isnt $ox_report->whensent, undef; + + $mech->submit_form_ok( { with_fields => { category => 'Graffiti' } }, 'form_submitted' ); + $ox_report->discard_changes; + is $ox_report->category, 'Graffiti'; + is $ox_report->whensent, undef; +}; + +}; + subtest 'change email to new user' => sub { $log_entries->delete; $mech->get_ok("/admin/report_edit/$report_id"); @@ -593,9 +624,13 @@ subtest 'change email to new user' => sub { state => $report->state, name => $report->name, email => $report->user->email, + category => 'Other', anonymous => 1, flagged => 'on', non_public => 'on', + external_id => '13', + external_body => '', + external_team => '', }; is_deeply( $mech->visible_form_values(), $fields, 'initial form values' ); @@ -845,6 +880,10 @@ for my $test ( }; } +my $westminster = $mech->create_body_ok(2504, 'Westminster City Council'); +$report->bodies_str($westminster->id); +$report->update; + for my $test ( { desc => 'user is problem owner', @@ -853,18 +892,18 @@ for my $test ( update_fixed => 0, update_reopen => 0, update_state => undef, - user_council => undef, + user_body => undef, content => 'user is problem owner', }, { - desc => 'user is council user', + desc => 'user is body user', problem_user => $user, update_user => $user2, update_fixed => 0, update_reopen => 0, update_state => undef, - user_council => 2504, - content => 'user is from same council as problem - 2504', + user_body => $westminster->id, + content => 'user is from same council as problem - ' . $westminster->id, }, { desc => 'update changed problem state', @@ -873,7 +912,7 @@ for my $test ( update_fixed => 0, update_reopen => 0, update_state => 'planned', - user_council => 2504, + user_body => $westminster->id, content => 'Update changed problem state to planned', }, { @@ -883,7 +922,7 @@ for my $test ( update_fixed => 1, update_reopen => 0, update_state => undef, - user_council => undef, + user_body => undef, content => 'Update marked problem as fixed', }, { @@ -893,7 +932,7 @@ for my $test ( update_fixed => 0, update_reopen => 1, update_state => undef, - user_council => undef, + user_body => undef, content => 'Update reopened problem', }, ) { @@ -907,7 +946,7 @@ for my $test ( $update->mark_open( $test->{update_reopen} ); $update->update; - $test->{update_user}->from_body( $test->{user_council} ); + $test->{update_user}->from_body( $test->{user_body} ); $test->{update_user}->update; $mech->get_ok('/admin/update_edit/' . $update->id ); @@ -1033,41 +1072,47 @@ subtest 'report search' => sub { $update->user($report->user); $update->update; - $mech->get_ok('/admin/search_reports'); - $mech->get_ok('/admin/search_reports?search=' . $report->id ); + $mech->get_ok('/admin/reports'); + $mech->get_ok('/admin/reports?search=' . $report->id ); $mech->content_contains( $report->title ); my $r_id = $report->id; - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id/">$r_id</a>} ); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); - $mech->get_ok('/admin/search_reports?search=' . $report->user->email); + $mech->get_ok('/admin/reports?search=' . $report->external_id); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + + $mech->get_ok('/admin/reports?search=ref:' . $report->external_id); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + + $mech->get_ok('/admin/reports?search=' . $report->user->email); my $u_id = $update->id; - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id/">$r_id</a>} ); - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id/#update_$u_id">$u_id</a>} ); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id#update_$u_id"[^>]*>$u_id</a>} ); $update->state('hidden'); $update->update; - $mech->get_ok('/admin/search_reports?search=' . $report->user->email); + $mech->get_ok('/admin/reports?search=' . $report->user->email); $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $u_id \s* </td>}xs ); $report->state('hidden'); $report->update; - $mech->get_ok('/admin/search_reports?search=' . $report->user->email); - $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $r_id \s* </td>}xs ); + $mech->get_ok('/admin/reports?search=' . $report->user->email); + $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td[^>]*> \s* $r_id \s* </td>}xs ); $report->state('fixed - user'); $report->update; - $mech->get_ok('/admin/search_reports?search=' . $report->user->email); - $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id/">$r_id</a>} ); + $mech->get_ok('/admin/reports?search=' . $report->user->email); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); }; subtest 'search abuse' => sub { - $mech->get_ok( '/admin/search_users?search=example' ); - $mech->content_like(qr/test4\@example.com.*\n.*\n.*Email in abuse table/); + $mech->get_ok( '/admin/users?search=example' ); + $mech->content_like(qr{test4\@example.com.*</td>\s*<td>.*?</td>\s*<td>\(Email in abuse table}s); }; subtest 'show flagged entries' => sub { @@ -1077,27 +1122,53 @@ subtest 'show flagged entries' => sub { $user->flagged( 1 ); $user->update; - $mech->get_ok('/admin/list_flagged'); + $mech->get_ok('/admin/flagged'); $mech->content_contains( $report->title ); $mech->content_contains( $user->email ); }; +my $haringey = $mech->create_body_ok(2509, 'Haringey Borough Council'); + subtest 'user search' => sub { - $mech->get_ok('/admin/search_users'); - $mech->get_ok('/admin/search_users?search=' . $user->name); + $mech->get_ok('/admin/users'); + $mech->get_ok('/admin/users?search=' . $user->name); $mech->content_contains( $user->name); my $u_id = $user->id; $mech->content_like( qr{user_edit/$u_id">Edit</a>} ); - $mech->get_ok('/admin/search_users?search=' . $user->email); + $mech->get_ok('/admin/users?search=' . $user->email); $mech->content_like( qr{user_edit/$u_id">Edit</a>} ); - $user->from_body(2509); + $user->from_body($haringey->id); $user->update; - $mech->get_ok('/admin/search_users?search=2509' ); - $mech->content_contains(2509); + $mech->get_ok('/admin/users?search=' . $haringey->id ); + $mech->content_contains('Haringey'); +}; + +subtest 'search does not show user from another council' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->get_ok('/admin/users'); + $mech->get_ok('/admin/users?search=' . $user->name); + + $mech->content_contains( "Searching found no users." ); + + $mech->get_ok('/admin/users?search=' . $user->email); + $mech->content_contains( "Searching found no users." ); + }; +}; + +subtest 'user_edit does not show user from another council' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->get('/admin/user_edit/' . $user->id); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404"; + }; }; $log_entries = FixMyStreet::App->model('DB::AdminLog')->search( @@ -1115,101 +1186,375 @@ is $log_entries->count, 0, 'no admin log entries'; $user->flagged( 0 ); $user->update; -for my $test ( - { - desc => 'edit user name', - fields => { - name => 'Test User', - email => 'test@example.com', - council => 2509, - flagged => undef, - }, - changes => { - name => 'Changed User', - }, - log_count => 1, - log_entries => [qw/edit/], - }, - { - desc => 'edit user email', - fields => { - name => 'Changed User', - email => 'test@example.com', - council => 2509, - flagged => undef, - }, - changes => { - email => 'changed@example.com', +my $southend = $mech->create_body_ok(2607, 'Southend-on-Sea Borough Council'); + +my %default_perms = ( + "permissions[moderate]" => undef, + "permissions[planned_reports]" => undef, + "permissions[report_edit]" => undef, + "permissions[report_edit_category]" => undef, + "permissions[report_edit_priority]" => undef, + "permissions[report_inspect]" => undef, + "permissions[report_instruct]" => undef, + "permissions[contribute_as_another_user]" => undef, + "permissions[contribute_as_body]" => undef, + "permissions[view_body_contribute_details]" => undef, + "permissions[user_edit]" => undef, + "permissions[user_manage_permissions]" => undef, + "permissions[user_assign_body]" => undef, + "permissions[user_assign_areas]" => undef, + "permissions[template_edit]" => undef, + "permissions[responsepriority_edit]" => undef, + "permissions[category_edit]" => undef, + trusted_bodies => undef, +); + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', +}, sub { + for my $test ( + { + desc => 'edit user name', + fields => { + name => 'Test User', + email => 'test@example.com', + body => $haringey->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + name => 'Changed User', + }, + log_count => 1, + log_entries => [qw/edit/], }, - log_count => 2, - log_entries => [qw/edit edit/], - }, - { - desc => 'edit user council', - fields => { - name => 'Changed User', - email => 'changed@example.com', - council => 2509, - flagged => undef, + { + desc => 'edit user email', + fields => { + name => 'Changed User', + email => 'test@example.com', + body => $haringey->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + email => 'changed@example.com', + }, + log_count => 2, + log_entries => [qw/edit edit/], }, - changes => { - council => 2607, + { + desc => 'edit user body', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $haringey->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + body => $southend->id, + }, + log_count => 3, + log_entries => [qw/edit edit edit/], }, - log_count => 3, - log_entries => [qw/edit edit edit/], - }, - { - desc => 'edit user flagged', - fields => { - name => 'Changed User', - email => 'changed@example.com', - council => 2607, - flagged => undef, + { + desc => 'edit user flagged', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $southend->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + flagged => 'on', + }, + log_count => 4, + log_entries => [qw/edit edit edit edit/], }, - changes => { - flagged => 'on', + { + desc => 'edit user remove flagged', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $southend->id, + phone => '', + flagged => 'on', + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + flagged => undef, + }, + log_count => 4, + log_entries => [qw/edit edit edit edit/], }, - log_count => 4, - log_entries => [qw/edit edit edit edit/], - }, - { - desc => 'edit user remove flagged', - fields => { - name => 'Changed User', - email => 'changed@example.com', - council => 2607, - flagged => 'on', + { + desc => 'edit user add is_superuser', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $southend->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + is_superuser => 'on', + }, + removed => [ + keys %default_perms, + ], + log_count => 5, + log_entries => [qw/edit edit edit edit edit/], }, - changes => { - flagged => undef, + { + desc => 'edit user remove is_superuser', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $southend->id, + phone => '', + flagged => undef, + is_superuser => 'on', + area_id => '', + }, + changes => { + is_superuser => undef, + }, + added => { + %default_perms, + }, + log_count => 5, + log_entries => [qw/edit edit edit edit edit/], }, - log_count => 4, - log_entries => [qw/edit edit edit edit/], - }, -) { - subtest $test->{desc} => sub { - $mech->get_ok( '/admin/user_edit/' . $user->id ); + ) { + subtest $test->{desc} => sub { + $mech->get_ok( '/admin/user_edit/' . $user->id ); - my $visible = $mech->visible_form_values; - is_deeply $visible, $test->{fields}, 'expected user'; + my $visible = $mech->visible_form_values; + is_deeply $visible, $test->{fields}, 'expected user'; - my $expected = { - %{ $test->{fields} }, - %{ $test->{changes} } + my $expected = { + %{ $test->{fields} }, + %{ $test->{changes} } + }; + + $mech->submit_form_ok( { with_fields => $expected } ); + + # Some actions cause visible fields to be added/removed + foreach my $x (@{ $test->{removed} }) { + delete $expected->{$x}; + } + if ( $test->{added} ) { + $expected = { + %$expected, + %{ $test->{added} } + }; + } + + $visible = $mech->visible_form_values; + is_deeply $visible, $expected, 'user updated'; + + $mech->content_contains( 'Updated!' ); }; + } +}; - $mech->submit_form_ok( { with_fields => $expected } ); +subtest "Test setting a report from unconfirmed to something else doesn't cause a front end error" => sub { + $report->update( { confirmed => undef, state => 'unconfirmed', non_public => 0 } ); + $mech->get_ok("/admin/report_edit/$report_id"); + $mech->submit_form_ok( { with_fields => { state => 'investigating' } } ); + $report->discard_changes; + ok( $report->confirmed, 'report has a confirmed timestamp' ); + $mech->get_ok("/report/$report_id"); +}; + +subtest "Check admin_base_url" => sub { + my $rs = FixMyStreet::App->model('DB::Problem'); + my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($report->cobrand)->new(); + + is ($report->admin_url($cobrand), + (sprintf 'http://www.example.org/admin/report_edit/%d', $report_id), + 'get_admin_url OK'); +}; + +# Finished with the superuser tests +$mech->log_out_ok; + +subtest "Users without from_body can't access admin" => sub { + $user->from_body( undef ); + $user->update; + + $mech->log_in_ok( $user->email ); - $visible = $mech->visible_form_values; - is_deeply $visible, $expected, 'user updated'; + ok $mech->get('/admin'); + is $mech->res->code, 403, "got 403"; - $mech->content_contains( 'Updated!' ); + $mech->log_out_ok; +}; + +subtest "Users with from_body can access their own council's admin" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->log_in_ok( $oxfordshireuser->email ); + + $mech->get_ok('/admin'); + $mech->content_contains( 'FixMyStreet admin:' ); + + $mech->log_out_ok; }; -} +}; + +subtest "Users with from_body can't access another council's admin" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'bristol' ], + }, sub { + $mech->log_in_ok( $oxfordshireuser->email ); + + ok $mech->get('/admin'); + is $mech->res->code, 403, "got 403"; -$mech->delete_user( $user ); -$mech->delete_user( $user2 ); -$mech->delete_user( $user3 ); -$mech->delete_user( 'test4@example.com' ); + $mech->log_out_ok; + }; +}; + +subtest "Users with from_body can't access fixmystreet.com admin" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + }, sub { + $mech->log_in_ok( $oxfordshireuser->email ); + + ok $mech->get('/admin'); + is $mech->res->code, 403, "got 403"; + + $mech->log_out_ok; + }; +}; + +subtest "response templates can be added" => sub { + is $oxfordshire->response_templates->count, 0, "No response templates yet"; + $mech->log_in_ok( $superuser->email ); + $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); + + my $fields = { + title => "Report acknowledgement", + text => "Thank you for your report. We will respond shortly.", + auto_response => undef, + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + + is $oxfordshire->response_templates->count, 1, "Response template was added"; +}; + +subtest "response templates are included on page" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $report->update({ category => $oxfordshirecontact->category, bodies_str => $oxfordshire->id }); + $mech->log_in_ok( $oxfordshireuser->email ); + + $mech->get_ok("/report/" . $report->id); + $mech->content_contains( $oxfordshire->response_templates->first->text ); + + $mech->log_out_ok; + }; +}; + +$mech->log_in_ok( $superuser->email ); -done_testing(); +subtest "response priorities can be added" => sub { + is $oxfordshire->response_priorities->count, 0, "No response priorities yet"; + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id . "/new" ); + + my $fields = { + name => "Cat 1A", + description => "Fixed within 24 hours", + deleted => undef, + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + + is $oxfordshire->response_priorities->count, 1, "Response template was added to body"; + is $oxfordshirecontact->response_priorities->count, 1, "Response template was added to contact"; +}; + +subtest "response priorities can be listed" => sub { + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); + + $mech->content_contains( $oxfordshire->response_priorities->first->name ); + $mech->content_contains( $oxfordshire->response_priorities->first->description ); +}; + +subtest "response priorities are limited by body" => sub { + my $bromleypriority = $bromley->response_priorities->create( { + deleted => 0, + name => "Bromley Cat 0", + } ); + + is $bromley->response_priorities->count, 1, "Response template was added to Bromley"; + is $oxfordshire->response_priorities->count, 1, "Response template wasn't added to Oxfordshire"; + + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); + $mech->content_lacks( $bromleypriority->name ); + + $mech->get_ok( "/admin/responsepriorities/" . $bromley->id ); + $mech->content_contains( $bromleypriority->name ); +}; + +$mech->log_out_ok; + +subtest "response priorities can't be viewed across councils" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'responsepriority_edit', + }); + $mech->log_in_ok( $oxfordshireuser->email ); + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); + $mech->content_contains( $oxfordshire->response_priorities->first->name ); + + + $mech->get( "/admin/responsepriorities/" . $bromley->id ); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404"; + + my $bromley_priority_id = $bromley->response_priorities->first->id; + $mech->get( "/admin/responsepriorities/" . $bromley->id . "/" . $bromley_priority_id ); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404"; + }; +}; + +END { + $mech->delete_user( $user ); + $mech->delete_user( $user2 ); + $mech->delete_user( $user3 ); + $mech->delete_user( $superuser ); + $mech->delete_user( 'test4@example.com' ); + $mech->delete_body( $oxfordshire ); + $mech->delete_body( $oxford ); + $mech->delete_body( $bromley ); + $mech->delete_body( $westminster ); + done_testing(); +} diff --git a/t/app/controller/admin_permissions.t b/t/app/controller/admin_permissions.t new file mode 100644 index 000000000..dd256173d --- /dev/null +++ b/t/app/controller/admin_permissions.t @@ -0,0 +1,205 @@ +use strict; +use warnings; +use Test::More; + +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council', id => 2237); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); + +my $bromley = $mech->create_body_ok(2482, 'Bromley Council', id => 2482); + +END { + $mech->delete_user( $user ); + $mech->delete_user( $user2 ); + $mech->delete_user( $superuser ); + $mech->delete_user( $oxfordshireuser ); +} + +my $dt = DateTime->new( + year => 2011, + month => 04, + day => 16, + hour => 15, + minute => 47, + second => 23 +); + +my ($report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Test', { + areas => ',2237,', +}); +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +$mech->log_in_ok( $oxfordshireuser->email ); + +subtest "Users can't edit report without report_edit permission" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->get("/admin/report_edit/$report_id"); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404, can't edit report without report_edit permission"; + }; +}; + +subtest "Users can edit report with report_edit permission" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'report_edit', + }); + + $mech->get_ok("/admin/report_edit/$report_id"); + $mech->content_contains( $report->title ); + }; +}; + +subtest "Users can't edit another council's reports with their own council's report_edit permission" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $report->bodies_str($bromley->id); + $report->cobrand('bromley'); + $report->update; + + $mech->get("/admin/report_edit/$report_id"); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404, can't edit report with incorrect body in report_edit permission"; + }; +}; + + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => [ 'oxfordshire' ], +}, sub { + my $user2_id = $user2->id; + $report->update({ bodies_str => $oxfordshire->id }); + + foreach my $perm (0, 1) { + if ($perm) { + $oxfordshireuser->user_body_permissions->find_or_create({ + body => $oxfordshire, + permission_type => 'user_edit', + }); + } + foreach my $report_user ($user, $user2) { + $report->update({ user => $report_user }); + foreach my $from_body (undef, $bromley, $oxfordshire) { + $user2->update({ from_body => $from_body }); + my $result = ($from_body || '') eq $oxfordshire || $report->user eq $user2 ? ($perm ? 200 : 404 ) : 404; + my $u = $result == 200 ? 'can' : 'cannot'; + my $b = $from_body ? $from_body->name : 'no body'; + my $p = $perm ? 'with' : 'without'; + my $r = $report->user eq $user2 ? 'with' : 'without'; + subtest "User $u edit user for $b $p permission, $r cobrand relation" => sub { + $mech->get("/admin/user_edit/$user2_id"); + my $success = $mech->res->is_success(); + ok $result == 200 ? $success : !$success, "got correct response"; + is $mech->res->code, $result, "got $result"; + }; + } + } + } + + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'user_assign_body', + }); + + subtest "Users can edit users of their own council" => sub { + $mech->get_ok("/admin/user_edit/$user2_id"); + $mech->content_contains( $user2->name ); + + # We shouldn't be able to see the permissions tick boxes + $mech->content_lacks('Moderate report details'); + + $mech->submit_form_ok( { with_fields => { + name => 'Test Updated User 2', + email => $user2->email, + body => $user2->from_body->id, + phone => '', + flagged => undef, + } } ); + $user2->discard_changes; + is $user2->name, 'Test Updated User 2', 'name changed'; + }; + + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'user_manage_permissions', + }); + + subtest "Users can edit permissions" => sub { + is $user2->user_body_permissions->count, 0, 'user2 has no permissions'; + + $mech->get_ok("/admin/user_edit/$user2_id"); + $mech->content_contains('Moderate report details'); + + $mech->submit_form_ok( { with_fields => { + name => $user2->name, + email => $user2->email, + body => $user2->from_body->id, + phone => '', + flagged => undef, + "permissions[moderate]" => 'on', + "permissions[report_edit_category]" => undef, + "permissions[report_edit_priority]" => undef, + "permissions[report_inspect]" => undef, + "permissions[report_instruct]" => undef, + "permissions[contribute_as_another_user]" => undef, + "permissions[contribute_as_body]" => undef, + "permissions[user_edit]" => undef, + "permissions[user_manage_permissions]" => undef, + "permissions[user_assign_areas]" => undef, + } } ); + + ok $user2->has_body_permission_to("moderate"), "user2 has been granted moderate permission"; + }; + + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'user_assign_areas', + }); + + subtest "Unsetting user from_body removes all permissions and area " => sub { + is $user2->user_body_permissions->count, 1, 'user2 has 1 permission'; + + $mech->get_ok("/admin/user_edit/$user2_id"); + $mech->content_contains('Moderate report details'); + + $mech->submit_form_ok( { with_fields => { + name => $user2->name, + email => $user2->email, + body => undef, + phone => '', + flagged => undef, + "permissions[moderate]" => 'on', # NB tick box is left on deliberately + "permissions[report_edit_category]" => undef, + "permissions[report_edit_priority]" => undef, + "permissions[report_inspect]" => undef, + "permissions[report_instruct]" => undef, + "permissions[contribute_as_another_user]" => undef, + "permissions[contribute_as_body]" => undef, + "permissions[user_edit]" => undef, + "permissions[user_manage_permissions]" => undef, + "permissions[user_assign_areas]" => undef, + } } ); + + is $user2->user_body_permissions->count, 0, 'user2 has had permissions removed'; + is $user2->area_id, undef, 'user2 has had area removed'; + }; +}; + +$mech->log_out_ok; + +done_testing(); diff --git a/t/app/controller/alert.t b/t/app/controller/alert.t index 3d95bef6d..cb5949b8f 100644 --- a/t/app/controller/alert.t +++ b/t/app/controller/alert.t @@ -1,12 +1,12 @@ use strict; use warnings; use Test::More; +use LWP::Protocol::PSGI; +use FixMyStreet::TestMech; +my $mech = FixMyStreet::TestMech->new; -use Catalyst::Test 'FixMyStreet::App'; -use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App'; - -ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' ); +use t::Mock::Nominatim; # check that we can get the page $mech->get_ok('/alert'); @@ -15,52 +15,65 @@ $mech->content_contains('Local RSS feeds and email alerts'); $mech->content_contains('html class="no-js" lang="en-gb"'); # check that we can get list page -$mech->get_ok('/alert/list'); -$mech->title_like(qr/^Local RSS feeds and email alerts/); -$mech->content_contains('Local RSS feeds and email alerts'); -$mech->content_contains('html class="no-js" lang="en-gb"'); - -$mech->get_ok('/alert/list?pc=EH99 1SP'); -$mech->title_like(qr/^Local RSS feeds and email alerts/); -$mech->content_contains('Here are the types of local problem alerts for ‘EH99 1SP’'); -$mech->content_contains('html class="no-js" lang="en-gb"'); -$mech->content_contains('Problems within 8.5km'); -$mech->content_contains('rss/pc/EH991SP/2'); -$mech->content_contains('rss/pc/EH991SP/5'); -$mech->content_contains('rss/pc/EH991SP/10'); -$mech->content_contains('rss/pc/EH991SP/20'); -$mech->content_contains('Problems within City of Edinburgh'); -$mech->content_contains('Problems within City Centre ward'); -$mech->content_contains('/rss/reports/City+of+Edinburgh'); -$mech->content_contains('/rss/reports/City+of+Edinburgh/City+Centre'); -$mech->content_contains('council:2651:City_of_Edinburgh'); -$mech->content_contains('ward:2651:20728:City_of_Edinburgh:City_Centre'); - -$mech->get_ok('/alert/list?pc=High Street'); -$mech->content_contains('We found more than one match for that location'); - -$mech->get_ok('/alert/list?pc='); -$mech->content_contains('To find out what local alerts we have for you'); - -$mech->get_ok('/alert/list?pc=GL502PR'); -$mech->content_contains('Problems within the boundary of'); - -$mech->get_ok('/alert/subscribe?rss=1&type=local&pc=ky16+8yg&rss=Give+me+an+RSS+feed&rznvy=' ); -$mech->content_contains('Please select the feed you want'); - -$mech->get_ok('/alert/subscribe?rss=1&feed=invalid:1000:A_Locationtype=local&pc=ky16+8yg&rss=Give+me+an+RSS+feed&rznvy='); -$mech->content_contains('Illegal feed selection'); - -$mech->get_ok('/alert/subscribe?rss=1&feed=area:1000:Birmingham'); -is $mech->uri->path, '/rss/reports/Birmingham'; - -$mech->get_ok('/alert/subscribe?rss=1&feed=area:1000:1001:Cheltenham:Lansdown'); -is $mech->uri->path, '/rss/area/Cheltenham/Lansdown'; - -$mech->get_ok('/alert/subscribe?rss=1&feed=council:1000:Gloucestershire'); -is $mech->uri->path, '/rss/reports/Gloucestershire'; - -$mech->get_ok('/alert/subscribe?rss=1&feed=ward:1000:1001:Cheltenham:Lansdown'); -is $mech->uri->path, '/rss/reports/Cheltenham/Lansdown'; +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + GEOCODER => '', +}, sub { + $mech->get_ok('/alert/list'); + $mech->title_like(qr/^Local RSS feeds and email alerts/); + $mech->content_contains('Local RSS feeds and email alerts'); + $mech->content_contains('html class="no-js" lang="en-gb"'); + + $mech->get_ok('/alert/list?pc=EH1 1BB'); + $mech->title_like(qr/^Local RSS feeds and email alerts/); + $mech->content_contains('Here are the types of local problem alerts for ‘EH1 1BB’'); + $mech->content_contains('html class="no-js" lang="en-gb"'); + $mech->content_contains('Problems within 10.0km'); + $mech->content_contains('rss/pc/EH11BB/2'); + $mech->content_contains('rss/pc/EH11BB/5'); + $mech->content_contains('rss/pc/EH11BB/10'); + $mech->content_contains('rss/pc/EH11BB/20'); + $mech->content_contains('Problems within Edinburgh City'); + $mech->content_contains('Problems within City Centre ward'); + $mech->content_contains('/rss/reports/Edinburgh'); + $mech->content_contains('/rss/reports/Edinburgh/City+Centre'); + $mech->content_contains('council:2651:Edinburgh'); + $mech->content_contains('ward:2651:20728:Edinburgh:City_Centre'); + + subtest "Test Nominatim lookup" => sub { + LWP::Protocol::PSGI->register(t::Mock::Nominatim->run_if_script, host => 'nominatim.openstreetmap.org'); + $mech->get_ok('/alert/list?pc=High Street'); + $mech->content_contains('We found more than one match for that location'); + }; + + $mech->get_ok('/alert/list?pc='); + $mech->content_contains('To find out what local alerts we have for you'); + + $mech->get_ok('/alert/list?pc=GL502PR'); + $mech->content_contains('Problems within the boundary of'); + + $mech->get_ok('/alert/subscribe?rss=1&type=local&pc=ky16+8yg&rss=Give+me+an+RSS+feed&rznvy=' ); + $mech->content_contains('Please select the feed you want'); + + $mech->get_ok('/alert/subscribe?rss=1&feed=invalid:1000:A_Locationtype=local&pc=ky16+8yg&rss=Give+me+an+RSS+feed&rznvy='); + $mech->content_contains('Illegal feed selection'); + + $mech->create_body_ok(2504, 'Birmingham City Council'); + $mech->create_body_ok(2226, 'Gloucestershire County Council'); + $mech->create_body_ok(2326, 'Cheltenham Borough Council'); + + $mech->get_ok('/alert/subscribe?rss=1&feed=area:1000:Birmingham'); + is $mech->uri->path, '/rss/reports/Birmingham'; + + $mech->get_ok('/alert/subscribe?rss=1&feed=area:1000:1001:Cheltenham:Lansdown'); + is $mech->uri->path, '/rss/area/Cheltenham/Lansdown'; + + $mech->get_ok('/alert/subscribe?rss=1&feed=council:1000:Gloucestershire'); + is $mech->uri->path, '/rss/reports/Gloucestershire'; + + $mech->get_ok('/alert/subscribe?rss=1&feed=ward:1000:1001:Cheltenham:Lansdown'); + is $mech->uri->path, '/rss/reports/Cheltenham/Lansdown'; +}; done_testing(); diff --git a/t/app/controller/alert_new.t b/t/app/controller/alert_new.t index c849b9485..ea38f7c25 100644 --- a/t/app/controller/alert_new.t +++ b/t/app/controller/alert_new.t @@ -3,15 +3,20 @@ use warnings; use Test::More; use FixMyStreet::TestMech; +use FixMyStreet::App; my $mech = FixMyStreet::TestMech->new; +$mech->log_in_ok('test@example.com'); +$mech->get_ok('/alert/subscribe?id=1'); +my ($csrf) = $mech->content =~ /name="token" value="([^"]*)"/; + foreach my $test ( { email => 'test@example.com', type => 'area_problems', - content => 'your alert will not be activated', - email_text => 'confirm the alert', + content => 'Click the link in our confirmation email to activate your alert', + email_text => "confirms that you'd like to receive an email", uri => '/alert/subscribe?type=local&rznvy=test@example.com&feed=area:1000:A_Location', param1 => 1000 @@ -19,8 +24,8 @@ foreach my $test ( { email => 'test@example.com', type => 'council_problems', - content => 'your alert will not be activated', - email_text => 'confirm the alert', + content => 'Click the link in our confirmation email to activate your alert', + email_text => "confirms that you'd like to receive an email", uri => '/alert/subscribe?type=local&rznvy=test@example.com&feed=council:1000:A_Location', param1 => 1000, @@ -29,8 +34,8 @@ foreach my $test ( { email => 'test@example.com', type => 'ward_problems', - content => 'your alert will not be activated', - email_text => 'confirm the alert', + content => 'Click the link in our confirmation email to activate your alert', + email_text => "confirms that you'd like to receive an email", uri => '/alert/subscribe?type=local&rznvy=test@example.com&feed=ward:1000:1001:A_Location:Diff_Location', param1 => 1000, @@ -39,8 +44,8 @@ foreach my $test ( { email => 'test@example.com', type => 'local_problems', - content => 'your alert will not be activated', - email_text => 'confirm the alert', + content => 'Click the link in our confirmation email to activate your alert', + email_text => "confirms that you'd like to receive an email", uri => '/alert/subscribe?type=local&rznvy=test@example.com&feed=local:10.2:20.1', param1 => 20.1, @@ -49,8 +54,8 @@ foreach my $test ( { email => 'test@example.com', type => 'new_updates', - content => 'your alert will not be activated', - email_text => 'confirm the alert', + content => 'Click the link in our confirmation email to activate your alert', + email_text => "confirms that you'd like to receive an email", uri => '/alert/subscribe?type=updates&rznvy=test@example.com&id=1', param1 => 1, } @@ -70,7 +75,7 @@ foreach my $test ( $mech->delete_user($user); } - $mech->get_ok( $test->{uri} ); + $mech->get_ok( $test->{uri} . "&token=$csrf" ); $mech->content_contains( $test->{content} ); $user = @@ -93,9 +98,10 @@ foreach my $test ( my $email = $mech->get_email; ok $email, "got an email"; - like $email->body, qr/$test->{email_text}/i, "Correct email text"; + like $mech->get_text_body_from_email($email), qr/$test->{email_text}/i, "Correct email text"; - my ( $url, $url_token ) = $email->body =~ m{(http://\S+/A/)(\S+)}; + my $url = $mech->get_link_from_email($email); + my ($url_token) = $url =~ m{/A/(\S+)}; ok $url, "extracted confirm url '$url'"; my $token = FixMyStreet::App->model('DB::Token')->find( @@ -112,12 +118,10 @@ foreach my $test ( my $existing_id = $alert->id; my $existing_token = $url_token; - $mech->get_ok( $test->{uri} ); + $mech->get_ok( $test->{uri} . "&token=$csrf" ); - $email = $mech->get_email; - ok $email, 'got a second email'; - - ($url_token) = $email->body =~ m{http://\S+/A/(\S+)}; + $url = $mech->get_link_from_email; + ($url_token) = $url =~ m{/A/(\S+)}; ok $url_token ne $existing_token, 'sent out a new token'; $token = FixMyStreet::App->model('DB::Token')->find( @@ -131,7 +135,7 @@ foreach my $test ( ok $token->data->{id} == $existing_id, 'subscribed to existing alert'; $mech->get_ok("/A/$url_token"); - $mech->content_contains('successfully confirmed'); + $mech->content_contains('alert created'); $alert = FixMyStreet::App->model('DB::Alert')->find( { id => $existing_id, } ); @@ -151,9 +155,7 @@ foreach my $test ( my $type = 'area_problems'; - my $user = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'test-new@example.com' } ); + my $user = $mech->create_user_ok('test-new@example.com'); my $alert = FixMyStreet::App->model('DB::Alert')->find( { @@ -164,7 +166,7 @@ foreach my $test ( # clear existing data so we can be sure we're creating it ok $alert->delete() if $alert && !$test->{exist}; - $mech->get_ok( '/alert/subscribe?type=local&rznvy=test-new@example.com&feed=area:1000:A_Location' ); + $mech->get_ok( '/alert/subscribe?type=local&rznvy=test-new@example.com&feed=area:1000:A_Location&token=' . $csrf ); $alert = FixMyStreet::App->model('DB::Alert')->find( { @@ -199,14 +201,17 @@ foreach my $test ( subtest $test->{desc} => sub { my $type = $test->{type} . '_problems'; - my $user = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => $test->{email} } ); + my $user = $mech->create_user_ok($test->{email}); $mech->log_in_ok( $test->{email} ); $mech->clear_emails_ok; - $mech->get_ok('/alert/list?pc=EH991SP'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/alert/list?pc=EH991SP'); + }; $mech->set_visible( [ radio => 'council:2651:City_of_Edinburgh' ] ); $mech->click('alert'); @@ -230,7 +235,7 @@ for my $test ( { email => 'test@example.com', type => 'new_updates', - content => 'your alert will not be activated', + content => 'Click the link in our confirmation email to activate your alert', email_text => 'confirm the alert', uri => '/alert/subscribe?type=updates&rznvy=test@example.com&id=1', param1 => 1, @@ -256,7 +261,7 @@ for my $test ( FixMyStreet::App->model('DB::Abuse') ->find_or_create( { email => $test->{email} } ); - $mech->get_ok( $test->{uri} ); + $mech->get_ok( $test->{uri} . "&token=$csrf" ); $mech->content_contains( $test->{content} ); $user = @@ -277,29 +282,8 @@ for my $test ( ok $alert, "Found the alert"; - my $email = $mech->get_email; - ok $email, "got an email"; - like $email->body, qr/$test->{email_text}/i, "Correct email text"; - - my ( $url, $url_token ) = $email->body =~ m{(http://\S+/A/)(\S+)}; - ok $url, "extracted confirm url '$url'"; - - my $token = FixMyStreet::App->model('DB::Token')->find( - { - token => $url_token, - scope => 'alert' - } - ); - ok $token, 'Token found in database'; - ok $alert->id == $token->data->{id}, 'token alertid matches alert id'; - $mech->clear_emails_ok; - $mech->get_ok("/A/$url_token"); - $mech->content_contains('error confirming'); - - $alert->discard_changes; - ok !$alert->confirmed, 'alert not set to confirmed'; $abuse->delete; @@ -307,43 +291,48 @@ for my $test ( }; } +$mech->create_body_ok(2226, 'Gloucestershire County Council'); +$mech->create_body_ok(2326, 'Cheltenham Borough Council'); + subtest "Test two-tier council alerts" => sub { for my $alert ( { feed => "local:51.896269:-2.093063", result => '/rss/l/51.896269,-2.093063' }, { feed => "area:2326:Cheltenham", result => '/rss/area/Cheltenham' }, { feed => "area:2326:4544:Cheltenham:Lansdown", result => '/rss/area/Cheltenham/Lansdown' }, { feed => "area:2226:Gloucestershire", result => '/rss/area/Gloucestershire' }, - { feed => "area:2226:14949:Gloucestershire:Lansdown%2C_Park_and_Warden_Hill", - result => '/rss/area/Gloucestershire/Lansdown%2C+Park+and+Warden+Hill' + { feed => "area:2226:14949:Gloucestershire:Lansdown_and_Park", + result => '/rss/area/Gloucestershire/Lansdown+and+Park' }, { feed => "council:2326:Cheltenham", result => '/rss/reports/Cheltenham' }, { feed => "ward:2326:4544:Cheltenham:Lansdown", result => '/rss/reports/Cheltenham/Lansdown' }, { feed => "council:2226:Gloucestershire", result => '/rss/reports/Gloucestershire' }, - { feed => "ward:2226:14949:Gloucestershire:Lansdown%2C_Park_and_Warden_Hill", - result => '/rss/reports/Gloucestershire/Lansdown%2C+Park+and+Warden+Hill' + { feed => "ward:2226:14949:Gloucestershire:Lansdown_and_Park", + result => '/rss/reports/Gloucestershire/Lansdown+and+Park' }, ) { - $mech->get_ok( '/alert/list?pc=GL502PR' ); - $mech->submit_form_ok( { - button => 'rss', - with_fields => { - feed => $alert->{feed}, - } - } ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok( '/alert/list?pc=GL502PR' ); + $mech->submit_form_ok( { + button => 'rss', + with_fields => { + feed => $alert->{feed}, + } + } ); + }; is $mech->uri->path, $alert->{result}, 'Redirected to right RSS feed'; } }; subtest "Test normal alert signups and that alerts are sent" => sub { - my $user1 = FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'reporter@example.com', name => 'Reporter User' } ); - ok $user1, "created test user"; - $user1->alerts->delete; + $mech->delete_user( 'reporter@example.com' ); + $mech->delete_user( 'alerts@example.com' ); - my $user2 = FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'alerts@example.com', name => 'Alert User' } ); - ok $user2, "created test user"; - $user2->alerts->delete; + my $user1 = $mech->create_user_ok('reporter@example.com', name => 'Reporter User' ); + + my $user2 = $mech->create_user_ok('alerts@example.com', name => 'Alert User' ); for my $alert ( { @@ -355,34 +344,41 @@ subtest "Test normal alert signups and that alerts are sent" => sub { }, { fields => { - feed => 'council:2651:City_of_Edinburgh', + feed => 'area:2651:City_of_Edinburgh', } }, ) { $mech->get_ok( '/alert' ); - $mech->submit_form_ok( { with_fields => { pc => 'EH11BB' } } ); - $mech->submit_form_ok( { - button => 'alert', - with_fields => $alert->{fields}, - } ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'EH11BB' } } ); + $mech->submit_form_ok( { + button => 'alert', + with_fields => $alert->{fields}, + } ); + }; if ( $alert->{email_confirm} ) { - my $email = $mech->get_email; + my $url = $mech->get_link_from_email; + my ($url_token) = $url =~ m{/A/(\S+)}; $mech->clear_emails_ok; - my ( $url, $url_token ) = $email->body =~ m{http://\S+(/A/(\S+))}; my $token = FixMyStreet::App->model('DB::Token')->find( { token => $url_token, scope => 'alert' } ); $mech->get_ok( $url ); - $mech->content_contains('successfully confirmed'); + $mech->content_contains('alert created'); } else { - $mech->content_contains('successfully created'); + $mech->content_contains('alert created'); } } - my $dt = DateTime->now()->add( days => 2); + my $dt = DateTime->now(time_zone => 'Europe/London')->add(days => 2); + + my $dt_parser = FixMyStreet::App->model('DB')->schema->storage->datetime_parser; my $report_time = '2011-03-01 12:00:00'; my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { postcode => 'EH1 1BB', - council => '2651', + bodies_str => '1', areas => ',11808,135007,14419,134935,2651,20728,', category => 'Street lighting', title => 'Testing', @@ -391,9 +387,9 @@ subtest "Test normal alert signups and that alerts are sent" => sub { name => $user1->name, anonymous => 0, state => 'fixed - user', - confirmed => $dt, - lastupdate => $dt, - whensent => $dt->clone->add( minutes => 5 ), + confirmed => $dt_parser->format_datetime($dt), + lastupdate => $dt_parser->format_datetime($dt), + whensent => $dt_parser->format_datetime($dt->clone->add( minutes => 5 )), lang => 'en-gb', service => '', cobrand => 'default', @@ -439,36 +435,149 @@ subtest "Test normal alert signups and that alerts are sent" => sub { $update_id = $update->id; ok $update, "created test update - $update_id"; - FixMyStreet::App->model('DB::AlertType')->email_alerts(); + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + FixMyStreet::App->model('DB::AlertType')->email_alerts(); + }; # TODO Note the below will fail if the db has an existing alert that matches $mech->email_count_is(3); my @emails = $mech->get_email; my $count; for (@emails) { - $count++ if $_->body =~ /The following updates have been left on this problem:/; - $count++ if $_->body =~ /The following new problems have been reported to City of\s*Edinburgh Council:/; - $count++ if $_->body =~ /The following nearby problems have been added:/; - $count++ if $_->body =~ /\s+-\s+Testing/; + my $body = $mech->get_text_body_from_email($_); + $count++ if $body =~ /The following updates have been left on this report:/; + $count++ if $body =~ /The following new FixMyStreet reports have been added in the Area 2651 area:/; + $count++ if $body =~ /The following FixMyStreet reports have been made within the area you\s+specified:/; + $count++ if $body =~ /\s+-\s+Testing/; } is $count, 5, 'Three emails, with five matching lines in them'; my $email = $emails[0]; - like $email->body, qr/Other User/, 'Update name given'; - unlike $email->body, qr/Anonymous User/, 'Update name not given'; + my $body = $mech->get_text_body_from_email($email); + like $body, qr/Other User/, 'Update name given'; + unlike $body, qr/Anonymous User/, 'Update name not given'; - # The update alert was to the problem reporter, so has a login update URL + # The update alert was to the problem reporter, so has a special update URL + $mech->log_out_ok; $mech->get_ok( "/report/$report_id" ); $mech->content_lacks( 'has not been fixed' ); - my ($url) = $email->body =~ m{(http://\S+/M/\S+)}; - ok $url, "extracted update url '$url'"; - $mech->get_ok( $url ); + my @urls = $mech->get_link_from_email($email, 1); + ok $urls[0] =~ m{/R/\S+}, "extracted update url '$urls[0]'"; + $mech->get_ok( $urls[0] ); is $mech->uri->path, "/report/" . $report_id, "redirected to report page"; $mech->content_contains( 'has not been fixed' ); - $mech->logged_in_ok; + $mech->not_logged_in_ok; + + ok $urls[-1] =~ m{/A/\S+}, "unsubscribe URL '$urls[-1]'"; + $mech->get_ok( $urls[-1] ); + $mech->content_contains('alert deleted'); + $mech->not_logged_in_ok; + + $mech->delete_user($user1); + $mech->delete_user($user2); +}; + +subtest "Test signature template is used from cobrand" => sub { + $mech->delete_user( 'reporter@example.com' ); + $mech->delete_user( 'alerts@example.com' ); + + my $user1 = $mech->create_user_ok('reporter@example.com', name => 'Reporter User' ); + + my $user2 = $mech->create_user_ok('alerts@example.com', name => 'Alert User' ); + + my $dt = DateTime->now(time_zone => 'Europe/London')->add(days => 2); + + my $dt_parser = FixMyStreet::App->model('DB')->schema->storage->datetime_parser; + + my $report_time = '2011-03-01 12:00:00'; + my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { + postcode => 'EH1 1BB', + bodies_str => '2651', + areas => ',11808,135007,14419,134935,2651,20728,', + category => 'Street lighting', + title => 'Testing', + detail => 'Testing Detail', + used_map => 1, + name => $user1->name, + anonymous => 0, + state => 'fixed - user', + confirmed => $dt_parser->format_datetime($dt), + lastupdate => $dt_parser->format_datetime($dt), + whensent => $dt_parser->format_datetime($dt->clone->add( minutes => 5 )), + lang => 'en-gb', + service => '', + cobrand => 'default', + cobrand_data => '', + send_questionnaire => 1, + latitude => '55.951963', + longitude => '-3.189944', + user_id => $user1->id, + } ); + my $report_id = $report->id; + ok $report, "created test report - $report_id"; + + my $alert = FixMyStreet::App->model('DB::Alert')->create( { + parameter => $report_id, + alert_type => 'new_updates', + user => $user1, + cobrand => 'default', + } ); + my $ret = $alert->confirm; + ok $ret, 'created alert for reporter'; + + my $update = FixMyStreet::App->model('DB::Comment')->create( { + problem_id => $report_id, + user_id => $user2->id, + name => 'Other User', + mark_fixed => 'false', + text => 'This is some update text', + state => 'confirmed', + confirmed => $dt->clone->add( hours => 7 ), + anonymous => 'f', + } ); + my $update_id = $update->id; + ok $update, "created test update - $update_id"; - ($url) = $emails[0]->body =~ m{http://\S+(/A/\S+)}; - $mech->get_ok( $url ); - $mech->content_contains('successfully deleted'); + + $mech->clear_emails_ok; + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + }, sub { + FixMyStreet::App->model('DB::AlertType')->email_alerts(); + }; + + my $email = $mech->get_text_body_from_email; + like $email, qr/All the best/, 'default signature used'; + unlike $email, qr/twitter.com/, 'nothing from fixmystreet signature'; + + $update = FixMyStreet::App->model('DB::Comment')->create( { + problem_id => $report_id, + user_id => $user2->id, + name => 'Anonymous User', + mark_fixed => 'true', + text => 'This is some more update text', + state => 'confirmed', + confirmed => $dt->clone->add( hours => 8 ), + anonymous => 't', + } ); + $update_id = $update->id; + ok $update, "created test update - $update_id"; + + $alert->cobrand('fixmystreet'); + $alert->update; + + $mech->clear_emails_ok; + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + }, sub { + FixMyStreet::App->model('DB::AlertType')->email_alerts(); + }; + + $email = $mech->get_text_body_from_email; + like $email, qr/twitter.com/, 'fixmystreet signature used'; $mech->delete_user($user1); $mech->delete_user($user2); @@ -509,15 +618,12 @@ for my $test ( }, ) { subtest $test->{desc} => sub { - my $user1 = FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'reporter@example.com', name => 'Reporter User' } ); - ok $user1, "created test user"; - $user1->alerts->delete; + $mech->delete_user( 'reporter@example.com' ); + $mech->delete_user( 'alerts@example.com' ); + + my $user1 = $mech->create_user_ok('reporter@example.com', name => 'Reporter User'); - my $user2 = FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'alerts@example.com', name => 'Alert User' } ); - ok $user2, "created test user"; - $user2->alerts->delete; + my $user2 = $mech->create_user_ok('alerts@example.com', name => 'Alert User'); my $dt = DateTime->now->add( minutes => -30 ); my $r_dt = $dt->clone->add( minutes => 20 ); @@ -530,9 +636,11 @@ for my $test ( my $alert_user1 = FixMyStreet::App->model('DB::Alert')->create( $alert_params ); ok $alert_user1, "alert created"; + my $dt_parser = FixMyStreet::App->model('DB')->schema->storage->datetime_parser; + my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { postcode => 'EH1 1BB', - council => '2651', + bodies_str => '2651', areas => ',11808,135007,14419,134935,2651,20728,', category => 'Street lighting', title => 'Alert test for non public reports', @@ -541,9 +649,9 @@ for my $test ( name => $user2->name, anonymous => 0, state => 'confirmed', - confirmed => $r_dt, - lastupdate => $r_dt, - whensent => $r_dt->clone->add( minutes => 5 ), + confirmed => $dt_parser->format_datetime($r_dt), + lastupdate => $dt_parser->format_datetime($r_dt), + whensent => $dt_parser->format_datetime($r_dt->clone->add( minutes => 5 )), lang => 'en-gb', service => '', cobrand => 'default', @@ -556,14 +664,21 @@ for my $test ( } ); $mech->clear_emails_ok; - FixMyStreet::App->model('DB::AlertType')->email_alerts(); + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + FixMyStreet::App->model('DB::AlertType')->email_alerts(); + }; $mech->email_count_is(0); $report->update( { non_public => 0 } ); - FixMyStreet::App->model('DB::AlertType')->email_alerts(); - $mech->email_count_is(1); - my $email = $mech->get_email; - like $email->body, qr/Alert\s+test\s+for\s+non\s+public\s+reports/, 'alert contains public report'; + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + FixMyStreet::App->model('DB::AlertType')->email_alerts(); + }; + my $email = $mech->get_text_body_from_email; + like $email, qr/Alert\s+test\s+for\s+non\s+public\s+reports/, 'alert contains public report'; $mech->delete_user( $user1 ); $mech->delete_user( $user2 ); @@ -571,26 +686,23 @@ for my $test ( } subtest 'check new updates alerts for non public reports only go to report owner' => sub { - my $user1 = FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'reporter@example.com', name => 'Reporter User' } ); - ok $user1, "created test user"; - $user1->alerts->delete; + $mech->delete_user( 'reporter@example.com' ); + $mech->delete_user( 'alerts@example.com' ); - my $user2 = FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'alerts@example.com', name => 'Alert User' } ); - ok $user2, "created test user"; - $user2->alerts->delete; + my $user1 = $mech->create_user_ok('reporter@example.com', name => 'Reporter User'); - my $user3 = FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'updates@example.com', name => 'Update User' } ); - ok $user3, "created test user"; + my $user2 = $mech->create_user_ok('alerts@example.com', name => 'Alert User'); + + my $user3 = $mech->create_user_ok('updates@example.com', name => 'Update User'); my $dt = DateTime->now->add( minutes => -30 ); my $r_dt = $dt->clone->add( minutes => 20 ); + my $dt_parser = FixMyStreet::App->model('DB')->schema->storage->datetime_parser; + my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { postcode => 'EH1 1BB', - council => '2651', + bodies_str => '2651', areas => ',11808,135007,14419,134935,2651,20728,', category => 'Street lighting', title => 'Alert test for non public reports', @@ -599,9 +711,9 @@ subtest 'check new updates alerts for non public reports only go to report owner name => $user2->name, anonymous => 0, state => 'confirmed', - confirmed => $r_dt, - lastupdate => $r_dt, - whensent => $r_dt->clone->add( minutes => 5 ), + confirmed => $dt_parser->format_datetime($r_dt), + lastupdate => $dt_parser->format_datetime($r_dt), + whensent => $dt_parser->format_datetime($r_dt->clone->add( minutes => 5 )), lang => 'en-gb', service => '', cobrand => 'default', @@ -648,20 +760,102 @@ subtest 'check new updates alerts for non public reports only go to report owner ok $alert_user2, "alert created"; FixMyStreet::App->model('DB::AlertType')->email_alerts(); - $mech->email_count_is(1); - my $email = $mech->get_email; - like $email->body, qr/This is some more update text/, 'alert contains update text'; + my $email = $mech->get_text_body_from_email; + like $email, qr/This is some more update text/, 'alert contains update text'; $mech->clear_emails_ok; $report->update( { non_public => 0 } ); FixMyStreet::App->model('DB::AlertType')->email_alerts(); - $mech->email_count_is(1); - $email = $mech->get_email; - like $email->body, qr/This is some more update text/, 'alert contains update text'; + $email = $mech->get_text_body_from_email; + like $email, qr/This is some more update text/, 'alert contains update text'; + + $mech->delete_user( $user1 ); + $mech->delete_user( $user2 ); + $mech->delete_user( $user3 ); +}; + +subtest 'check setting inlude dates in new updates cobrand option' => sub { + my $include_date_in_alert_override= Sub::Override->new( + "FixMyStreet::Cobrand::Default::include_time_in_update_alerts", + sub { return 1; } + ); + $mech->delete_user( 'reporter@example.com' ); + $mech->delete_user( 'alerts@example.com' ); + + my $user1 = $mech->create_user_ok('reporter@example.com', name => 'Reporter User'); + + my $user2 = $mech->create_user_ok('alerts@example.com', name => 'Alert User'); + + my $user3 = $mech->create_user_ok('updates@example.com', name => 'Update User'); + + my $dt = DateTime->now->add( minutes => -30 ); + my $r_dt = $dt->clone->add( minutes => 20 ); + + my $dt_parser = FixMyStreet::App->model('DB')->schema->storage->datetime_parser; + + my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { + postcode => 'EH1 1BB', + bodies_str => '2651', + areas => ',11808,135007,14419,134935,2651,20728,', + category => 'Street lighting', + title => 'Alert test for non public reports', + detail => 'Testing Detail', + used_map => 1, + name => $user2->name, + anonymous => 0, + state => 'confirmed', + confirmed => $dt_parser->format_datetime($r_dt), + lastupdate => $dt_parser->format_datetime($r_dt), + whensent => $dt_parser->format_datetime($r_dt->clone->add( minutes => 5 )), + lang => 'en-gb', + service => '', + cobrand => 'default', + cobrand_data => '', + send_questionnaire => 1, + latitude => '55.951963', + longitude => '-3.189944', + user_id => $user2->id, + } ); + + my $update = FixMyStreet::App->model('DB::Comment')->create( { + problem_id => $report->id, + user_id => $user3->id, + name => 'Anonymous User', + mark_fixed => 'false', + text => 'This is some more update text', + state => 'confirmed', + confirmed => $r_dt->clone->add( minutes => 8 ), + anonymous => 't', + } ); + + my $alert_user1 = FixMyStreet::App->model('DB::Alert')->create( { + user => $user1, + alert_type => 'new_updates', + parameter => $report->id, + confirmed => 1, + whensubscribed => $dt, + } ); + ok $alert_user1, "alert created"; + + + $mech->clear_emails_ok; + FixMyStreet::App->model('DB::AlertType')->email_alerts(); + + # if we don't do this then we're applying the date inflation code and + # it's timezone munging to the DateTime object above and not the DateTime + # object that's inflated from the database value and these turn out to be + # different as the one above has a UTC timezone and not the floating one + # that those from the DB do. + $update->discard_changes(); + + my $date_in_alert = Utils::prettify_dt( $update->confirmed ); + my $email = $mech->get_text_body_from_email; + like $email, qr/$date_in_alert/, 'alert contains date'; $mech->delete_user( $user1 ); $mech->delete_user( $user2 ); $mech->delete_user( $user3 ); + $include_date_in_alert_override->restore(); }; done_testing(); diff --git a/t/app/controller/around.t b/t/app/controller/around.t index d973543ce..c8aca04aa 100644 --- a/t/app/controller/around.t +++ b/t/app/controller/around.t @@ -7,14 +7,18 @@ my $mech = FixMyStreet::TestMech->new; subtest "check that if no query we get sent back to the homepage" => sub { $mech->get_ok('/around'); - is $mech->uri->path, '/around', "still at '/around'"; + is $mech->uri->path, '/', "redirected to '/'"; }; # x,y requests were generated by the old map code. We keep the behavior for # historic links subtest "redirect x,y requests to lat/lon (301 - permanent)" => sub { - $mech->get_ok('/around?x=3281&y=1113'); + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/around?x=3281&y=1113'); + }; # did we redirect to lat,lon? is $mech->uri->path, '/around', "still on /around"; @@ -50,8 +54,12 @@ foreach my $test ( { subtest "test bad pc value '$test->{pc}'" => sub { $mech->get_ok('/'); - $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, - "bad location" ); + FixMyStreet::override_config { + GEOCODER => '', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, + "bad location" ); + }; is_deeply $mech->page_errors, $test->{errors}, "expected errors for pc '$test->{pc}'"; is_deeply $mech->pc_alternatives, $test->{pc_alternatives}, @@ -63,8 +71,8 @@ foreach my $test ( foreach my $test ( { pc => 'SW1A 1AA', - latitude => '51.50101', - longitude => '-0.141587', + latitude => '51.501009', + longitude => '-0.141588', }, { pc => 'TQ 388 773', @@ -75,8 +83,13 @@ foreach my $test ( { subtest "check lat/lng for '$test->{pc}'" => sub { $mech->get_ok('/'); - $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, - "good location" ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, + "good location" ); + }; is_deeply $mech->page_errors, [], "no errors for pc '$test->{pc}'"; is_deeply $mech->extract_location, $test, "got expected location for pc '$test->{pc}'"; @@ -85,16 +98,21 @@ foreach my $test ( subtest 'check non public reports are not displayed on around page' => sub { my $params = { - postcode => 'EH99 1SP', + postcode => 'EH1 1BB', latitude => 55.9519637512, longitude => -3.17492254484, }; my @edinburgh_problems = - $mech->create_problems_for_council( 5, 2651, 'Around page', $params ); + $mech->create_problems_for_body( 5, 2651, 'Around page', $params ); $mech->get_ok('/'); - $mech->submit_form_ok( { with_fields => { pc => 'EH99 1SP' } }, - "good location" ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, + "good location" ); + }; $mech->content_contains( 'Around page Test 3 for 2651', 'problem to be marked non public visible' ); @@ -102,11 +120,55 @@ subtest 'check non public reports are not displayed on around page' => sub { ok $private->update( { non_public => 1 } ), 'problem marked non public'; $mech->get_ok('/'); - $mech->submit_form_ok( { with_fields => { pc => 'EH99 1SP' } }, - "good location" ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, + "good location" ); + }; $mech->content_lacks( 'Around page Test 3 for 2651', 'problem marked non public is not visible' ); }; +subtest 'check category and status filtering works on /ajax' => sub { + my $categories = [ 'Pothole', 'Vegetation', 'Flytipping' ]; + my $params = { + postcode => 'OX1 1ND', + latitude => 51.7435918829363, + longitude => -1.23201966270446, + }; + my $bbox = ($params->{longitude} - 0.01) . ',' . ($params->{latitude} - 0.01) + . ',' . ($params->{longitude} + 0.01) . ',' . ($params->{latitude} + 0.01); + + # Create one open and one fixed report in each category + foreach my $category ( @$categories ) { + foreach my $state ( 'confirmed', 'fixed' ) { + my %report_params = ( + %$params, + category => $category, + state => $state, + ); + $mech->create_problems_for_body( 1, 2237, 'Around page', \%report_params ); + } + } + + my $json = $mech->get_ok_json( '/ajax?bbox=' . $bbox ); + my $pins = $json->{pins}; + is scalar @$pins, 6, 'correct number of reports when no filters'; + + $json = $mech->get_ok_json( '/ajax?filter_category=Pothole&bbox=' . $bbox ); + $pins = $json->{pins}; + is scalar @$pins, 2, 'correct number of Pothole reports'; + + $json = $mech->get_ok_json( '/ajax?status=open&bbox=' . $bbox ); + $pins = $json->{pins}; + is scalar @$pins, 3, 'correct number of open reports'; + + $json = $mech->get_ok_json( '/ajax?status=fixed&filter_category=Vegetation&bbox=' . $bbox ); + $pins = $json->{pins}; + is scalar @$pins, 1, 'correct number of fixed Vegetation reports'; +}; + done_testing(); diff --git a/t/app/controller/auth.t b/t/app/controller/auth.t index 67466e959..3a11cfc4a 100644 --- a/t/app/controller/auth.t +++ b/t/app/controller/auth.t @@ -2,16 +2,19 @@ use strict; use warnings; use Test::More; +use Test::MockModule; use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; my $test_email = 'test@example.com'; +my $test_email2 = 'test@example.net'; my $test_password = 'foobar'; $mech->delete_user($test_email); END { $mech->delete_user($test_email); + $mech->delete_user($test_email2); done_testing(); } @@ -31,6 +34,13 @@ for my $test ( ) { my ( $email, $error_message ) = @$test; + + my $resolver = Test::MockModule->new('Net::DNS::Resolver'); + $resolver->mock('send', sub { + my ($self, $domain, $type) = @_; + return Net::DNS::Packet->new; + }); + pass "--- testing bad email '$email' gives error '$error_message'"; $mech->get_ok('/auth'); is_deeply $mech->page_errors, [], 'no errors initially'; @@ -46,6 +56,10 @@ for my $test ( is_deeply $mech->page_errors, [ $error_message ], 'errors match'; } +# Email address parsing should pass from here +my $resolver = Test::MockModule->new('Email::Valid'); +$resolver->mock('address', sub { $_[1] }); + # create a new account $mech->clear_emails_ok; $mech->get_ok('/auth'); @@ -63,16 +77,14 @@ $mech->not_logged_in_ok; # check that we got one email { - $mech->email_count_is(1); my $email = $mech->get_email; $mech->clear_emails_ok; - is $email->header('Subject'), "Your FixMyStreet.com account details", + is $email->header('Subject'), "Your FixMyStreet account details", "subject is correct"; is $email->header('To'), $test_email, "to is correct"; # extract the link - my ($link) = $email->body =~ m{(http://\S+)}; - ok $link, "Found a link in email '$link'"; + my $link = $mech->get_link_from_email($email); # check that the user does not exist sub get_user { @@ -91,13 +103,8 @@ $mech->not_logged_in_ok; is $mech->uri->path, '/my', "redirected to the 'my' section of site"; $mech->logged_in_ok; - # logout and try to use the token again + # logout $mech->log_out_ok; - $mech->get_ok($link); - is $mech->uri, $link, "not logged in"; - $mech->content_contains( 'Link too old or already used', - 'token now invalid' ); - $mech->not_logged_in_ok; } # get a sign in email and change password @@ -121,10 +128,7 @@ $mech->not_logged_in_ok; # follow link and change password - check not prompted for old password $mech->not_logged_in_ok; - $mech->email_count_is(1); - my $email = $mech->get_email; - $mech->clear_emails_ok; - my ($link) = $email->body =~ m{(http://\S+)}; + my $link = $mech->get_link_from_email; $mech->get_ok($link); is $mech->uri->path, '/faq', "redirected to the Help page"; @@ -133,7 +137,7 @@ $mech->not_logged_in_ok; ok my $form = $mech->form_name('change_password'), "found change password form"; is_deeply [ sort grep { $_ } map { $_->name } $form->inputs ], # - [ 'confirm', 'new_password' ], + [ 'confirm', 'new_password', 'token' ], "check we got expected fields (ie not old_password)"; # check the various ways the form can be wrong @@ -180,6 +184,48 @@ $mech->not_logged_in_ok; ok $user->password, "user now has a password"; } +subtest "Test change email page" => sub { + # Still signed in from the above test + $mech->get_ok('/my'); + $mech->follow_link_ok({url => '/auth/change_email'}); + $mech->submit_form_ok( + { with_fields => { email => "" } }, + "submit blank change email form" + ); + $mech->content_contains( 'Please enter your email', "found expected error" ); + $mech->submit_form_ok({ with_fields => { email => $test_email2 } }, "change_email to $test_email2"); + is $mech->uri->path, '/auth/change_email', "still on change email page"; + $mech->content_contains( 'Now check your email', "found check your email" ); + my $link = $mech->get_link_from_email; + $mech->get_ok($link); + is $mech->uri->path, '/auth/change_email/success', "redirected to the change_email page"; + $mech->content_contains('successfully confirmed'); + ok(FixMyStreet::App->model('DB::User')->find( { email => $test_email2 } ), "got a user"); + + ok(FixMyStreet::App->model('DB::User')->create( { email => $test_email } ), "created old user"); + $mech->submit_form_ok({ with_fields => { email => $test_email } }, + "change_email back to $test_email" + ); + is $mech->uri->path, '/auth/change_email', "still on change email page"; + $mech->content_contains( 'Now check your email', "found check your email" ); + $link = $mech->get_link_from_email; + $mech->get_ok($link); + is $mech->uri->path, '/auth/change_email/success', "redirected to the change_email page"; + $mech->content_contains('successfully confirmed'); + + # Test you can't click the link if logged out + $mech->submit_form_ok({ with_fields => { email => $test_email } }, + "change_email back to $test_email" + ); + is $mech->uri->path, '/auth/change_email', "still on change email page"; + $mech->content_contains( 'Now check your email', "found check your email" ); + $link = $mech->get_link_from_email; + $mech->log_out_ok; + $mech->get_ok($link); + isnt $mech->uri->path, '/auth/change_email/success', "not redirected to the change_email page"; + $mech->content_contains('Sorry'); +}; + foreach my $remember_me ( '1', '0' ) { subtest "sign in using valid details (remember_me => '$remember_me')" => sub { $mech->get_ok('/auth'); @@ -193,7 +239,7 @@ foreach my $remember_me ( '1', '0' ) { }, button => 'sign_in', }, - "sign in with '$test_email' & '$test_password" + "sign in with '$test_email' & '$test_password'" ); is $mech->uri->path, '/my', "redirected to correct page"; @@ -218,10 +264,28 @@ $mech->submit_form_ok( }, button => 'sign_in', }, - "sign in with '$test_email' & '$test_password" + "sign in with '$test_email' & 'not the password'" ); is $mech->uri->path, '/auth', "redirected to correct page"; $mech->content_contains( 'problem with your email/password combination', 'found error message' ); +subtest "sign in but have email form autofilled" => sub { + $mech->get_ok('/auth'); + $mech->submit_form_ok( + { + form_name => 'general_auth', + fields => { + email => $test_email, + password_sign_in => $test_password, + name => 'Auto-completed from elsewhere', + }, + button => 'sign_in', + }, + "sign in with '$test_email' and auto-completed name" + ); + is $mech->uri->path, '/my', "redirected to correct page"; +}; + + # more test: # TODO: test that email are always lowercased diff --git a/t/app/controller/auth_social.t b/t/app/controller/auth_social.t new file mode 100644 index 000000000..09fdf22d3 --- /dev/null +++ b/t/app/controller/auth_social.t @@ -0,0 +1,259 @@ +use strict; +use warnings; +use Test::More; +use Test::MockModule; +use LWP::Protocol::PSGI; +use LWP::Simple; +use JSON::MaybeXS; + +use t::Mock::Facebook; +use t::Mock::Twitter; + +use FixMyStreet::TestMech; +my $mech = FixMyStreet::TestMech->new; + +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + +my ($report) = $mech->create_problems_for_body(1, '2345', 'Test'); + +FixMyStreet::override_config { + FACEBOOK_APP_ID => 'facebook-app-id', + TWITTER_KEY => 'twitter-key', + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', +}, sub { + +my $fb_email = 'facebook@example.org'; +my $fb_uid = 123456789; + +my $resolver = Test::MockModule->new('Email::Valid'); +$resolver->mock('address', sub { 'facebook@example.org' }); + +for my $fb_state ( 'refused', 'no email', 'existing UID', 'okay' ) { + for my $page ( 'my', 'report', 'update' ) { + subtest "test FB '$fb_state' login for page '$page'" => sub { + # Lots of user changes happening here, make sure we don't confuse + # Catalyst with a cookie session user that no longer exists + $mech->log_out_ok; + $mech->cookie_jar({}); + if ($fb_state eq 'existing UID') { + my $user = $mech->create_user_ok($fb_email); + $user->update({ facebook_id => $fb_uid }); + } else { + $mech->delete_user($fb_email); + } + + # Set up a mock to catch (most, see below) requests to Facebook + my $fb = t::Mock::Facebook->new; + $fb->returns_email(0) if $fb_state eq 'no email' || $fb_state eq 'existing UID'; + LWP::Protocol::PSGI->register($fb->to_psgi_app, host => 'www.facebook.com'); + LWP::Protocol::PSGI->register($fb->to_psgi_app, host => 'graph.facebook.com'); + + # Due to https://metacpan.org/pod/Test::WWW::Mechanize::Catalyst#External-Redirects-and-allow_external + # the redirect to Facebook's OAuth page can mess up the session + # cookie. So let's pretend we always on www.facebook.com, which + # sorts that out. + $mech->host('www.facebook.com'); + + # Fetch the page with the form via which we wish to log in + my $fields; + if ($page eq 'my') { + $mech->get_ok('/my'); + } elsif ($page eq 'report') { + $mech->get_ok('/'); + $mech->submit_form_ok( { with_fields => { pc => 'SW1A1AA' } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $fields = { + title => 'Test title', + detail => 'Test detail', + }; + } else { + $mech->get_ok('/report/' . $report->id); + $fields = { + update => 'Test update', + }; + } + $mech->submit_form(with_fields => $fields, button => 'facebook_sign_in'); + + # As well as the cookie issue above, caused by this external + # redirect rewriting the host, the redirect gets handled directly + # by Catalyst, not our mocked handler, so will be a 404. Check + # the redirect happened instead. + is $mech->res->previous->code, 302, 'FB button redirected'; + like $mech->res->previous->header('Location'), qr{facebook\.com.*dialog/oauth.*facebook-app-id}, 'FB redirect to oauth URL'; + + # Okay, now call the callback Facebook would send us to + if ($fb_state eq 'refused') { + $mech->get_ok('/auth/Facebook?error_code=ERROR'); + } else { + $mech->get_ok('/auth/Facebook?code=response-code'); + } + + # Check we're showing the right form, regardless of what came back + if ($page eq 'report') { + $mech->content_contains('/report/new'); + } elsif ($page eq 'update') { + $mech->content_contains('/report/update'); + } + + if ($fb_state eq 'refused') { + $mech->content_contains('Sorry, we could not log you in. Please fill in the form below.'); + $mech->not_logged_in_ok; + } elsif ($fb_state eq 'no email') { + $mech->content_contains('We need your email address, please give it below.'); + # We don't have an email, so check that we can still submit it, + # and the ID carries through the confirmation + if ($page eq 'update') { + $fields->{rznvy} = $fb_email; + } else { + $fields->{email} = $fb_email; + } + $fields->{name} = 'Ffion Tester'; + $mech->submit_form(with_fields => $fields); + $mech->content_contains('Nearly done! Now check your email'); + + my $url = $mech->get_link_from_email; + $mech->clear_emails_ok; + ok $url, "extracted confirm url '$url'"; + + my $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $fb_email } ); + if ($page eq 'my') { + is $user, undef, 'No user yet exists'; + } else { + is $user->facebook_id, undef, 'User has no facebook ID'; + } + $mech->get_ok( $url ); + $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $fb_email } ); + is $user->facebook_id, $fb_uid, 'User now has correct facebook ID'; + + } elsif ($page ne 'my') { + # /my auth login goes directly there, no message like this + $mech->content_contains('You have successfully signed in; please check and confirm your details are accurate'); + $mech->logged_in_ok; + } else { + is $mech->uri->path, '/my', 'Successfully on /my page'; + } + } + } +} + +$resolver->mock('address', sub { 'twitter@example.org' }); + +my $tw_email = 'twitter@example.org'; +my $tw_uid = 987654321; + +# Twitter has no way of getting the email, so no "okay" state here +for my $tw_state ( 'refused', 'existing UID', 'no email' ) { + for my $page ( 'my', 'report', 'update' ) { + subtest "test Twitter '$tw_state' login for page '$page'" => sub { + # Lots of user changes happening here, make sure we don't confuse + # Catalyst with a cookie session user that no longer exists + $mech->log_out_ok; + $mech->cookie_jar({}); + if ($tw_state eq 'existing UID') { + my $user = $mech->create_user_ok($tw_email); + $user->update({ twitter_id => $tw_uid }); + } else { + $mech->delete_user($tw_email); + } + + # Set up a mock to catch (most, see below) requests to Twitter + my $tw = t::Mock::Twitter->new; + LWP::Protocol::PSGI->register($tw->to_psgi_app, host => 'api.twitter.com'); + + # Due to https://metacpan.org/pod/Test::WWW::Mechanize::Catalyst#External-Redirects-and-allow_external + # the redirect to Twitter's OAuth page can mess up the session + # cookie. So let's pretend we always on api.twitter.com, which + # sorts that out. + $mech->host('api.twitter.com'); + + # Fetch the page with the form via which we wish to log in + my $fields; + if ($page eq 'my') { + $mech->get_ok('/my'); + } elsif ($page eq 'report') { + $mech->get_ok('/'); + $mech->submit_form_ok( { with_fields => { pc => 'SW1A1AA' } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $fields = { + title => 'Test title', + detail => 'Test detail', + }; + } else { + $mech->get_ok('/report/' . $report->id); + $fields = { + update => 'Test update', + }; + } + $mech->submit_form(with_fields => $fields, button => 'twitter_sign_in'); + + # As well as the cookie issue above, caused by this external + # redirect rewriting the host, the redirect gets handled directly + # by Catalyst, not our mocked handler, so will be a 404. Check + # the redirect happened instead. + is $mech->res->previous->code, 302, 'Twitter button redirected'; + like $mech->res->previous->header('Location'), qr{api\.twitter\.com/oauth/authenticate\?oauth_token=request-token}, 'Twitter redirect to oauth URL'; + + # Okay, now call the callback Facebook would send us to + if ($tw_state eq 'refused') { + $mech->get_ok('/auth/Twitter?denied=token'); + } else { + $mech->get_ok('/auth/Twitter?oauth_token=request-token&oauth_verifier=verifier'); + } + + # Check we're showing the right form, regardless of what came back + if ($page eq 'report') { + $mech->content_contains('/report/new'); + } elsif ($page eq 'update') { + $mech->content_contains('/report/update'); + } + + if ($tw_state eq 'refused') { + $mech->content_contains('Sorry, we could not log you in. Please fill in the form below.'); + $mech->not_logged_in_ok; + } elsif ($tw_state eq 'no email') { + $mech->content_contains('We need your email address, please give it below.'); + # We don't have an email, so check that we can still submit it, + # and the ID carries through the confirmation + if ($page eq 'update') { + $fields->{rznvy} = $tw_email; + } else { + $fields->{email} = $tw_email; + } + $fields->{name} = 'Ffion Tester'; + $mech->submit_form(with_fields => $fields); + $mech->content_contains('Nearly done! Now check your email'); + + my $url = $mech->get_link_from_email; + $mech->clear_emails_ok; + ok $url, "extracted confirm url '$url'"; + + my $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $tw_email } ); + if ($page eq 'my') { + is $user, undef, 'No user yet exists'; + } else { + is $user->twitter_id, undef, 'User has no twitter ID'; + } + $mech->get_ok( $url ); + $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $tw_email } ); + is $user->twitter_id, $tw_uid, 'User now has correct twitter ID'; + + } elsif ($page ne 'my') { + # /my auth login goes directly there, no message like this + $mech->content_contains('You have successfully signed in; please check and confirm your details are accurate'); + $mech->logged_in_ok; + } else { + is $mech->uri->path, '/my', 'Successfully on /my page'; + } + } + } +} + +}; + +END { + $mech->delete_problems_for_body('2345'); + done_testing(); +} diff --git a/t/app/controller/contact.t b/t/app/controller/contact.t index 11e0d30cf..7c2769b9c 100644 --- a/t/app/controller/contact.t +++ b/t/app/controller/contact.t @@ -8,7 +8,7 @@ my $mech = FixMyStreet::TestMech->new; $mech->get_ok('/contact'); $mech->title_like(qr/Contact Us/); -$mech->content_contains("We'd love to hear what you think about this site"); +$mech->content_contains("It's often quickest to "); my $problem_main; @@ -52,12 +52,7 @@ for my $test ( ) { subtest 'check reporting a problem displays correctly' => sub { - my $user = FixMyStreet::App->model('DB::User')->find_or_create( - { - name => $test->{name}, - email => $test->{email} - } - ); + my $user = $mech->create_user_ok($test->{email}, name => $test->{name}); my $problem = FixMyStreet::App->model('DB::Problem')->create( { @@ -80,12 +75,8 @@ for my $test ( if ( $test->{update} ) { my $update_info = $test->{update}; - my $update_user = FixMyStreet::App->model('DB::User')->find_or_create( - { - name => $update_info->{name}, - email => $update_info->{email} - } - ); + my $update_user = $mech->create_user_ok($update_info->{email}, + name => $update_info->{name}); $update = FixMyStreet::App->model('DB::Comment')->create( { @@ -93,7 +84,7 @@ for my $test ( user => $update_user, state => 'confirmed', text => $update_info->{text}, - confirmed => \'ms_current_timestamp()', + confirmed => \'current_timestamp', mark_fixed => 'f', anonymous => 'f', } @@ -259,17 +250,17 @@ for my $test ( $mech->get_ok('/contact'); } $mech->submit_form_ok( { with_fields => $test->{fields} } ); - $mech->content_contains('Thanks for your feedback'); - $mech->email_count_is(1); + $mech->content_contains('Thank you for your enquiry'); my $email = $mech->get_email; is $email->header('Subject'), 'FMS message: ' . $test->{fields}->{subject}, 'subject'; is $email->header('From'), "\"$test->{fields}->{name}\" <$test->{fields}->{em}>", 'from'; - like $email->body, qr/$test->{fields}->{message}/, 'body'; - like $email->body, qr/Sent by contact.cgi on \S+. IP address (?:\d{1,3}\.){3,}\d{1,3}/, 'body footer'; + my $body = $mech->get_text_body_from_email($email); + like $body, qr/$test->{fields}->{message}/, 'body'; + like $body, qr/Sent by contact.cgi on \S+. IP address (?:\d{1,3}\.){3,}\d{1,3}/, 'body footer'; my $problem_id = $test->{fields}{id}; - like $email->body, qr/Complaint about report $problem_id/, 'reporting a report' + like $body, qr/Complaint about report $problem_id/, 'reporting a report' if $test->{fields}{id}; $problem_main->discard_changes; @@ -282,6 +273,115 @@ for my $test ( }; } +for my $test ( + { + fields => { + em => 'test@example.com', + name => 'A name', + subject => 'A subject', + message => 'A message', + dest => undef, + }, + page_errors => + [ 'There were problems with your report. Please see below.', + 'Please enter who your message is for', + ] + }, + { + fields => { + em => 'test@example.com', + name => 'A name', + subject => 'A subject', + message => 'A message', + dest => 'council', + }, + page_errors => + [ 'There were problems with your report. Please see below.', + 'You can only contact the team behind FixMyStreet using our contact form', + ] + }, + { + fields => { + em => 'test@example.com', + name => 'A name', + subject => 'A subject', + message => 'A message', + dest => 'update', + }, + page_errors => + [ 'There were problems with your report. Please see below.', + 'You can only contact the team behind FixMyStreet using our contact form', + ] + }, + ) +{ + subtest 'check submit page incorrect destination handling' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + }, sub { + $mech->host('www.fixmystreet.com'); + $mech->get_ok( $test->{url} ? $test->{url} : '/contact' ); + $mech->submit_form_ok( { with_fields => $test->{fields} } ); + is_deeply $mech->page_errors, $test->{page_errors}, 'page errors'; + + # we santise this when we submit so need to remove it + delete $test->{fields}->{id} + if $test->{fields}->{id} and $test->{fields}->{id} eq 'invalid'; + is_deeply $mech->visible_form_values, $test->{fields}, 'form values'; + + if ( $test->{fields}->{dest} and $test->{fields}->{dest} eq 'update' ) { + $mech->content_contains( 'www.writetothem.com', 'includes link to WTT if trying to update report' ); + } elsif ( $test->{fields}->{dest} and $test->{fields}->{dest} eq 'council' ) { + $mech->content_lacks( 'www.writetothem.com', 'does not include link to WTT if trying to contact council' ); + $mech->content_contains( 'should find contact details', 'mentions checking council website for contact details' ); + } + } + }; +} + +for my $test ( + { + fields => { + em => 'test@example.com', + name => 'A name', + subject => 'A subject', + message => 'A message', + dest => 'help', + }, + }, + { + fields => { + em => 'test@example.com', + name => 'A name', + subject => 'A subject', + message => 'A message', + dest => 'feedback', + }, + }, + { + fields => { + em => 'test@example.com', + name => 'A name', + subject => 'A subject', + message => 'A message', + dest => 'from_council', + }, + }, + ) +{ + subtest 'check email sent correctly with dest field set to us' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + }, sub { + $mech->clear_emails_ok; + $mech->get_ok('/contact'); + $mech->submit_form_ok( { with_fields => $test->{fields} } ); + $mech->content_contains('Thank you for your enquiry'); + $mech->email_count_is(1); + } + }; +} + $problem_main->delete; done_testing(); diff --git a/t/app/controller/dashboard.t b/t/app/controller/dashboard.t index 307943abd..903affdcf 100644 --- a/t/app/controller/dashboard.t +++ b/t/app/controller/dashboard.t @@ -1,6 +1,7 @@ use strict; use warnings; use Test::More; +use Test::MockTime ':all'; use FixMyStreet::TestMech; use Web::Scraper; @@ -12,559 +13,613 @@ my $test_pass = 'password'; my $test_council = 2651; my $test_ward = 20723; -$mech->delete_user( $test_user ); -my $user = FixMyStreet::App->model('DB::User')->create( { - email => $test_user, - password => $test_pass, -} ); +my $body = $mech->create_body_ok($test_council, 'City of Edinburgh Council'); -my $p_user = FixMyStreet::App->model('DB::User')->find_or_create( { - email => 'p_user@example.com' -} ); +$mech->delete_user( $test_user ); +my $user = $mech->create_user_ok($test_user, password => $test_pass); -$mech->not_logged_in_ok; -$mech->get_ok('/dashboard'); +my $p_user = $mech->create_user_ok('p_user@example.com'); -$mech->content_contains( 'sign in' ); +# Dashboard tests assume we are not too early in year, to allow reporting +# within same year, as a convenience. +set_absolute_time('2014-03-01T12:00:00'); +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', +}, sub { -$mech->submit_form( - with_fields => { email => $test_user, password_sign_in => $test_pass } -); + $mech->not_logged_in_ok; + $mech->get_ok('/dashboard'); -is $mech->status, '404', 'If not council user get 404'; + $mech->content_contains( 'sign in' ); -$user->from_body( $test_council ); -$user->update; + $mech->submit_form( + with_fields => { email => $test_user, password_sign_in => $test_pass } + ); -$mech->log_out_ok; -$mech->get_ok('/dashboard'); -$mech->submit_form_ok( { - with_fields => { email => $test_user, password_sign_in => $test_pass } -} ); + is $mech->status, '404', 'If not council user get 404'; -$mech->content_contains( 'City of Edinburgh' ); + $user->from_body( $body->id ); + $user->update; -FixMyStreet::App->model('DB::Contact')->search( { body_id => $test_council } ) - ->delete; + $mech->log_out_ok; + $mech->get_ok('/dashboard'); + $mech->submit_form_ok( { + with_fields => { email => $test_user, password_sign_in => $test_pass } + } ); -delete_problems(); + $mech->content_contains( 'Area 2651' ); -my @cats = qw( Grafitti Litter Potholes Other ); -for my $contact ( @cats ) { - FixMyStreet::App->model('DB::Contact')->create( - { - body_id => $test_council, - category => $contact, - email => "$contact\@example.org", - confirmed => 1, - whenedited => DateTime->now, - deleted => 0, - editor => 'test', - note => 'test', - } - ); -} - -$mech->get_ok('/dashboard'); - -my $categories = scraper { - process "select[name=category] > option", 'cats[]' => 'TEXT', - process "select[name=ward] > option", 'wards[]' => 'TEXT', - process "table[id=overview] > tr", 'rows[]' => scraper { - process 'td', 'cols[]' => 'TEXT' - }, - process "tr[id=total] > td", 'totals[]' => 'TEXT', - process "tr[id=fixed_council] > td", 'council[]' => 'TEXT', - process "tr[id=fixed_user] > td", 'user[]' => 'TEXT', - process "tr[id=total_fixed] > td", 'total_fixed[]' => 'TEXT', - process "tr[id=in_progress] > td", 'in_progress[]' => 'TEXT', - process "tr[id=planned] > td", 'planned[]' => 'TEXT', - process "tr[id=investigating] > td", 'investigating[]' => 'TEXT', - process "tr[id=marked] > td", 'marked[]' => 'TEXT', - process "tr[id=avg_marked] > td", 'avg_marked[]' => 'TEXT', - process "tr[id=avg_fixed] > td", 'avg_fixed[]' => 'TEXT', - process "tr[id=not_marked] > td", 'not_marked[]' => 'TEXT', - process "tr[id=closed] > td", 'closed[]' => 'TEXT', - process "table[id=reports] > tr > td", 'report_lists[]' => scraper { - process 'ul > li', 'reports[]' => 'TEXT' - }, -}; + FixMyStreet::App->model('DB::Contact')->search( { body_id => $body->id } ) + ->delete; -my $expected_cats = [ 'All', '-- Pick a category --', @cats ]; -my $res = $categories->scrape( $mech->content ); -is_deeply( $res->{cats}, $expected_cats, 'correct list of categories' ); + delete_problems(); -foreach my $row ( @{ $res->{rows} }[1 .. 11] ) { - foreach my $col ( @{ $row->{cols} } ) { - is $col, 0; + my @cats = qw( Grafitti Litter Potholes Other ); + for my $contact ( @cats ) { + FixMyStreet::App->model('DB::Contact')->create( + { + body_id => $body->id, + category => $contact, + email => "$contact\@example.org", + confirmed => 1, + whenedited => DateTime->now, + deleted => 0, + editor => 'test', + note => 'test', + } + ); } -} -for my $reports ( @{ $res->{report_lists} } ) { - is_deeply $reports, {}, 'No reports'; -} + $mech->get_ok('/dashboard'); -foreach my $test ( - { - desc => 'confirmed today with no state', - dt => DateTime->now, - counts => [1,1,1,1], - report_counts => [1, 0, 0], - }, - { - desc => 'confirmed last 7 days with no state', - dt => DateTime->now->subtract( days => 6, hours => 23 ), - counts => [1,2,2,2], - report_counts => [2, 0, 0], - }, - { - desc => 'confirmed last 8 days with no state', - dt => DateTime->now->subtract( days => 8 ), - counts => [1,2,3,3], - report_counts => [2, 1, 0], - }, - { - desc => 'confirmed last 4 weeks with no state', - dt => DateTime->now->subtract( weeks => 2 ), - counts => [1,2,4,4], - report_counts => [2, 1, 1], - }, - { - desc => 'confirmed this year with no state', - dt => DateTime->now->subtract( weeks => 7 ), - counts => [1,2,4,5], - report_counts => [2, 1, 1], - }, -) { - subtest $test->{desc} => sub { - make_problem( { state => 'confirmed', conf_dt => $test->{dt} } ); - - $mech->get_ok('/dashboard'); - $res = $categories->scrape( $mech->content ); - - check_row( $res, 'totals', $test->{counts} ); - check_row( $res, 'not_marked', $test->{counts} ); - - check_report_counts( $res, $test->{report_counts} ); + my $categories = scraper { + process "select[name=category] > option", 'cats[]' => 'TEXT', + process "select[name=ward] > option", 'wards[]' => 'TEXT', + process "table[id=overview] > tr", 'rows[]' => scraper { + process 'td', 'cols[]' => 'TEXT' + }, + process "tr[id=total] > td", 'totals[]' => 'TEXT', + process "tr[id=fixed_council] > td", 'council[]' => 'TEXT', + process "tr[id=fixed_user] > td", 'user[]' => 'TEXT', + process "tr[id=total_fixed] > td", 'total_fixed[]' => 'TEXT', + process "tr[id=in_progress] > td", 'in_progress[]' => 'TEXT', + process "tr[id=action_scheduled] > td", 'action_scheduled[]' => 'TEXT', + process "tr[id=investigating] > td", 'investigating[]' => 'TEXT', + process "tr[id=marked] > td", 'marked[]' => 'TEXT', + process "tr[id=avg_marked] > td", 'avg_marked[]' => 'TEXT', + process "tr[id=avg_fixed] > td", 'avg_fixed[]' => 'TEXT', + process "tr[id=not_marked] > td", 'not_marked[]' => 'TEXT', + process "tr[id=closed] > td", 'closed[]' => 'TEXT', + process "table[id=reports] > tr > td", 'report_lists[]' => scraper { + process 'ul > li', 'reports[]' => 'TEXT' + }, }; -} - -delete_problems(); - -my $is_monday = DateTime->now->day_of_week == 1 ? 1 : 0; - -foreach my $test ( - { - desc => 'user fixed today', - confirm_dt => DateTime->now->subtract( days => 1 ), - mark_dt => DateTime->now, - state => 'fixed - user', - counts => { - totals => $is_monday ? [0,1,1,1] : [1,1,1,1], - user => [1,1,1,1], - council => [0,0,0,0], - avg_fixed => [0,0,0,0], - total_fixed => [1,1,1,1], - } - }, - { - desc => 'council fixed today', - confirm_dt => DateTime->now->subtract( days => 1 ), - mark_dt => DateTime->now, - state => 'fixed - council', - counts => { - totals => $is_monday ? [0,2,2,2] : [2,2,2,2], - user => [1,1,1,1], - council => [1,1,1,1], - avg_fixed => [1,1,1,1], - total_fixed => [2,2,2,2], - } - }, - { - desc => 'marked investigating today', - confirm_dt => DateTime->now->subtract( days => 1 ), - mark_dt => DateTime->now, - state => 'investigating', - counts => { - totals => $is_monday ? [0,3,3,3] : [3,3,3,3], - user => [1,1,1,1], - council => [1,1,1,1], - total_fixed => [2,2,2,2], - avg_marked => [1,1,1,1], - investigating => [1,1,1,1], - marked => [1,1,1,1] - } - }, - { - desc => 'marked in progress today', - confirm_dt => DateTime->now->subtract( days => 1 ), - mark_dt => DateTime->now, - state => 'in progress', - counts => { - totals => $is_monday ? [0,4,4,4] : [4,4,4,4], - user => [1,1,1,1], - council => [1,1,1,1], - total_fixed => [2,2,2,2], - avg_marked => [1,1,1,1], - investigating => [1,1,1,1], - in_progress => [1,1,1,1], - marked => [2,2,2,2] - } - }, - { - desc => 'marked as planned today', - confirm_dt => DateTime->now->subtract( days => 1 ), - mark_dt => DateTime->now, - state => 'planned', - counts => { - totals => $is_monday ? [ 0,5,5,5] : [5,5,5,5], - user => [1,1,1,1], - council => [1,1,1,1], - total_fixed => [2,2,2,2], - avg_marked => [1,1,1,1], - investigating => [1,1,1,1], - in_progress => [1,1,1,1], - planned => [1,1,1,1], - marked => [3,3,3,3] - } - }, - { - desc => 'marked as planned today, confirmed a week ago', - confirm_dt => DateTime->now->subtract( days => 8 ), - mark_dt => DateTime->now, - state => 'planned', - counts => { - totals => $is_monday ? [0,5,6,6] : [5,5,6,6], - user => [1,1,1,1], - council => [1,1,1,1], - total_fixed => [2,2,2,2], - avg_marked => [3,3,3,3], - investigating => [1,1,1,1], - in_progress => [1,1,1,1], - planned => [2,2,2,2], - marked => [4,4,4,4] - } - }, - { - desc => 'marked as council fixed today, confirmed a week ago', - confirm_dt => DateTime->now->subtract( days => 8 ), - mark_dt => DateTime->now, - state => 'fixed - council', - counts => { - totals => $is_monday ? [0,5,7,7] : [5,5,7,7], - user => [1,1,1,1], - council => [2,2,2,2], - total_fixed => [3,3,3,3], - avg_fixed => [5,5,5,5], - avg_marked => [3,3,3,3], - investigating => [1,1,1,1], - in_progress => [1,1,1,1], - planned => [2,2,2,2], - marked => [4,4,4,4] - } - }, - { - desc => 'marked as council fixed a week ago, confirmed 3 weeks ago', - confirm_dt => DateTime->now->subtract( days => 21), - mark_dt => DateTime->now->subtract( days => 8 ), - state => 'fixed - council', - counts => { - totals => $is_monday ? [0,5,8,8] : [5,5,8,8], - user => [1,1,1,1], - council => [2,2,3,3], - total_fixed => [3,3,4,4], - avg_fixed => [5,5,7,7], - avg_marked => [3,3,3,3], - investigating => [1,1,1,1], - in_progress => [1,1,1,1], - planned => [2,2,2,2], - marked => [4,4,4,4] - } - }, - { - desc => 'marked as user fixed 6 weeks ago, confirmed 7 weeks ago', - confirm_dt => DateTime->now->subtract( weeks => 6 ), - mark_dt => DateTime->now->subtract( weeks => 7 ), - state => 'fixed - user', - counts => { - totals => $is_monday ? [0,5,8,9] : [5,5,8,9], - user => [1,1,1,2], - council => [2,2,3,3], - total_fixed => [3,3,4,5], - avg_fixed => [5,5,7,7], - avg_marked => [3,3,3,3], - investigating => [1,1,1,1], - in_progress => [1,1,1,1], - planned => [2,2,2,2], - marked => [4,4,4,4] - } - }, - { - desc => 'marked as closed', - confirm_dt => DateTime->now->subtract( days => 1 ), - mark_dt => DateTime->now, - state => 'closed', - counts => { - totals => $is_monday ? [0,6,9,10] : [6,6,9,10], - user => [1,1,1,2], - council => [2,2,3,3], - total_fixed => [3,3,4,5], - avg_fixed => [5,5,7,7], - avg_marked => [2,2,2,2], - investigating => [1,1,1,1], - in_progress => [1,1,1,1], - planned => [2,2,2,2], - closed => [1,1,1,1], - marked => [5,5,5,5] - } - }, -) { - subtest $test->{desc} => sub { - make_problem( - { - state => $test->{state}, - conf_dt => $test->{confirm_dt}, - mark_dt => $test->{mark_dt}, - } - ); - $mech->get_ok('/dashboard'); - $res = $categories->scrape( $mech->content ); + my $expected_cats = [ 'All', '-- Pick a category --', @cats ]; + my $res = $categories->scrape( $mech->content ); + is_deeply( $res->{cats}, $expected_cats, 'correct list of categories' ); - foreach my $row ( keys %{ $test->{counts} } ) { - check_row( $res, $row, $test->{counts}->{$row} ); + foreach my $row ( @{ $res->{rows} }[1 .. 11] ) { + foreach my $col ( @{ $row->{cols} } ) { + is $col, 0; } - }; -} + } -delete_problems(); + for my $reports ( @{ $res->{report_lists} } ) { + is_deeply $reports, {}, 'No reports'; + } -for my $test ( - { - desc => 'Selecting no category does nothing', - p1 => { - state => 'confirmed', - conf_dt => DateTime->now(), - category => 'Potholes', + my $now = DateTime->now(time_zone => 'local'); + foreach my $test ( + { + desc => 'confirmed today with no state', + dt => $now, + counts => [1,1,1,1], + report_counts => [1, 0, 0], + }, + { + desc => 'confirmed last 7 days with no state', + dt => $now->clone->subtract( days => 6, hours => 23 ), + counts => [1,2,2,2], + report_counts => [2, 0, 0], }, - p2 => { - state => 'confirmed', - conf_dt => DateTime->now(), - category => 'Litter', + { + desc => 'confirmed last 8 days with no state', + dt => $now->clone->subtract( days => 8 ), + counts => [1,2,3,3], + report_counts => [2, 1, 0], }, - category => '', - counts => { - totals => [2,2,2,2], + { + desc => 'confirmed last 2 weeks with no state', + dt => $now->clone->subtract( weeks => 2 ), + counts => [1,2,4,4], + report_counts => [2, 1, 1], }, - counts_after => { - totals => [2,2,2,2], + { + desc => 'confirmed this year with no state', + dt => $now->clone->subtract( weeks => 7 ), + counts => [1,2,4,5], + report_counts => [2, 1, 1], + }, + ) { + subtest $test->{desc} => sub { + make_problem( { state => 'confirmed', conf_dt => $test->{dt} } ); + + $mech->get_ok('/dashboard'); + $res = $categories->scrape( $mech->content ); + + check_row( $res, 'totals', $test->{counts} ); + check_row( $res, 'not_marked', $test->{counts} ); + + check_report_counts( $res, $test->{report_counts} ); + }; + } + + delete_problems(); + + my $is_monday = DateTime->now->day_of_week == 1 ? 1 : 0; + + foreach my $test ( + { + desc => 'user fixed today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'fixed - user', + counts => { + totals => $is_monday ? [0,1,1,1] : [1,1,1,1], + user => [1,1,1,1], + council => [0,0,0,0], + avg_fixed => [0,0,0,0], + total_fixed => [1,1,1,1], + } }, - report_counts => [2,0,0], - report_counts_after => [2,0,0], - }, - { - desc => 'Limit display by category', - category => 'Potholes', - counts => { - totals => [2,2,2,2], + { + desc => 'council fixed today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'fixed - council', + counts => { + totals => $is_monday ? [0,2,2,2] : [2,2,2,2], + user => [1,1,1,1], + council => [1,1,1,1], + avg_fixed => [1,1,1,1], + total_fixed => [2,2,2,2], + } }, - counts_after => { - totals => [1,1,1,1], + { + desc => 'marked investigating today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'investigating', + counts => { + totals => $is_monday ? [0,3,3,3] : [3,3,3,3], + user => [1,1,1,1], + council => [1,1,1,1], + total_fixed => [2,2,2,2], + avg_marked => [1,1,1,1], + investigating => [1,1,1,1], + marked => [1,1,1,1] + } }, - report_counts => [2,0,0], - report_counts_after => [1,0,0], - }, - { - desc => 'Limit display for category with no entries', - category => 'Grafitti', - counts => { - totals => [2,2,2,2], + { + desc => 'marked in progress today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'in progress', + counts => { + totals => $is_monday ? [0,4,4,4] : [4,4,4,4], + user => [1,1,1,1], + council => [1,1,1,1], + total_fixed => [2,2,2,2], + avg_marked => [1,1,1,1], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + marked => [2,2,2,2] + } }, - counts_after => { - totals => [0,0,0,0], + { + desc => 'marked as action scheduled today', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'action scheduled', + counts => { + totals => $is_monday ? [ 0,5,5,5] : [5,5,5,5], + user => [1,1,1,1], + council => [1,1,1,1], + total_fixed => [2,2,2,2], + avg_marked => [1,1,1,1], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + action_scheduled => [1,1,1,1], + marked => [3,3,3,3] + } }, - report_counts => [2,0,0], - report_counts_after => [0,0,0], - }, - { - desc => 'Limit display by category for council fixed', - p1 => { - state => 'fixed - council', - conf_dt => DateTime->now()->subtract( weeks => 1 ), - mark_dt => DateTime->now()->subtract( weeks => 1 ), - category => 'Potholes', + { + desc => 'marked as action scheduled today, confirmed a week ago', + confirm_dt => DateTime->now->subtract( days => 8 ), + mark_dt => DateTime->now, + state => 'action scheduled', + counts => { + totals => $is_monday ? [0,5,6,6] : [5,5,6,6], + user => [1,1,1,1], + council => [1,1,1,1], + total_fixed => [2,2,2,2], + avg_marked => [3,3,3,3], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + action_scheduled => [2,2,2,2], + marked => [4,4,4,4] + } }, - p2 => { - state => 'fixed - council', - conf_dt => DateTime->now()->subtract( weeks => 1 ), - mark_dt => DateTime->now()->subtract( weeks => 1 ), - category => 'Litter', + { + desc => 'marked as council fixed today, confirmed a week ago', + confirm_dt => DateTime->now->subtract( days => 8 ), + mark_dt => DateTime->now, + state => 'fixed - council', + counts => { + totals => $is_monday ? [0,5,7,7] : [5,5,7,7], + user => [1,1,1,1], + council => [2,2,2,2], + total_fixed => [3,3,3,3], + avg_fixed => [5,5,5,5], + avg_marked => [3,3,3,3], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + action_scheduled => [2,2,2,2], + marked => [4,4,4,4] + } }, - category => 'Potholes', - counts => { - council => [0,0,2,2], - totals => [2,2,4,4], + { + desc => 'marked as council fixed a week ago, confirmed 3 weeks ago', + confirm_dt => DateTime->now->subtract( days => 21), + mark_dt => DateTime->now->subtract( days => 8 ), + state => 'fixed - council', + counts => { + totals => $is_monday ? [0,5,8,8] : [5,5,8,8], + user => [1,1,1,1], + council => [2,2,3,3], + total_fixed => [3,3,4,4], + avg_fixed => [5,5,7,7], + avg_marked => [3,3,3,3], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + action_scheduled => [2,2,2,2], + marked => [4,4,4,4] + } }, - counts_after => { - council => [0,0,1,1], - totals => [1,1,2,2], + { + desc => 'marked as user fixed 6 weeks ago, confirmed 7 weeks ago', + confirm_dt => DateTime->now->subtract( weeks => 6 ), + mark_dt => DateTime->now->subtract( weeks => 7 ), + state => 'fixed - user', + counts => { + totals => $is_monday ? [0,5,8,9] : [5,5,8,9], + user => [1,1,1,2], + council => [2,2,3,3], + total_fixed => [3,3,4,5], + avg_fixed => [5,5,7,7], + avg_marked => [3,3,3,3], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + action_scheduled => [2,2,2,2], + marked => [4,4,4,4] + } }, - report_counts => [2,2,0], - report_counts_after => [1,1,0], - }, - { - desc => 'Limit display by category for user fixed', - p1 => { - state => 'fixed - user', - conf_dt => DateTime->now()->subtract( weeks => 1 ), - mark_dt => DateTime->now()->subtract( weeks => 1 ), - category => 'Potholes', + { + desc => 'marked as closed', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'closed', + counts => { + totals => $is_monday ? [0,6,9,10] : [6,6,9,10], + user => [1,1,1,2], + council => [2,2,3,3], + total_fixed => [3,3,4,5], + avg_fixed => [5,5,7,7], + avg_marked => [2,2,2,2], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + action_scheduled => [2,2,2,2], + closed => [1,1,1,1], + marked => [5,5,5,5] + } }, - p2 => { - state => 'fixed - user', - conf_dt => DateTime->now()->subtract( weeks => 1 ), - mark_dt => DateTime->now()->subtract( weeks => 1 ), - category => 'Litter', + { + desc => 'marked as planned', + confirm_dt => DateTime->now->subtract( days => 1 ), + mark_dt => DateTime->now, + state => 'planned', + counts => { + totals => $is_monday ? [0,7,10,11] : [7,7,10,11], + user => [1,1,1,2], + council => [2,2,3,3], + total_fixed => [3,3,4,5], + avg_fixed => [5,5,7,7], + avg_marked => [2,2,2,2], + investigating => [1,1,1,1], + in_progress => [1,1,1,1], + action_scheduled => [3,3,3,3], + closed => [1,1,1,1], + marked => [6,6,6,6] + } }, - category => 'Potholes', - counts => { - user => [0,0,2,2], - council => [0,0,2,2], - totals => [2,2,6,6], + ) { + subtest $test->{desc} => sub { + make_problem( + { + state => $test->{state}, + conf_dt => $test->{confirm_dt}, + mark_dt => $test->{mark_dt}, + } + ); + + $mech->get_ok('/dashboard'); + $res = $categories->scrape( $mech->content ); + + foreach my $row ( keys %{ $test->{counts} } ) { + check_row( $res, $row, $test->{counts}->{$row} ); + } + }; + } + + delete_problems(); + + for my $test ( + { + desc => 'Selecting no category does nothing', + p1 => { + state => 'confirmed', + conf_dt => DateTime->now(), + category => 'Potholes', + }, + p2 => { + state => 'confirmed', + conf_dt => DateTime->now(), + category => 'Litter', + }, + category => '', + counts => { + totals => [2,2,2,2], + }, + counts_after => { + totals => [2,2,2,2], + }, + report_counts => [2,0,0], + report_counts_after => [2,0,0], }, - counts_after => { - user => [0,0,1,1], - council => [0,0,1,1], - totals => [1,1,3,3], + { + desc => 'Limit display by category', + category => 'Potholes', + counts => { + totals => [2,2,2,2], + }, + counts_after => { + totals => [1,1,1,1], + }, + report_counts => [2,0,0], + report_counts_after => [1,0,0], }, - report_counts => [2,4,0], - report_counts_after => [1,2,0], - }, - { - desc => 'Limit display by ward', - p1 => { - state => 'confirmed', - conf_dt => DateTime->now()->subtract( weeks => 1 ), - category => 'Potholes', - # in real life it has commas around it and the search - # uses them - areas => ',20720,', + { + desc => 'Limit display for category with no entries', + category => 'Grafitti', + counts => { + totals => [2,2,2,2], + }, + counts_after => { + totals => [0,0,0,0], + }, + report_counts => [2,0,0], + report_counts_after => [0,0,0], }, - p2 => { - state => 'fixed - council', - conf_dt => DateTime->now()->subtract( weeks => 1 ), - mark_dt => DateTime->now()->subtract( weeks => 1 ), - category => 'Litter', - areas => ',20720,', + { + desc => 'Limit display by category for council fixed', + p1 => { + state => 'fixed - council', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Potholes', + }, + p2 => { + state => 'fixed - council', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Litter', + }, + category => 'Potholes', + counts => { + council => [0,0,2,2], + totals => [2,2,4,4], + }, + counts_after => { + council => [0,0,1,1], + totals => [1,1,2,2], + }, + report_counts => [2,2,0], + report_counts_after => [1,1,0], }, - ward => 20720, - counts => { - user => [0,0,2,2], - council => [0,0,3,3], - totals => [2,2,8,8], + { + desc => 'Limit display by category for user fixed', + p1 => { + state => 'fixed - user', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Potholes', + }, + p2 => { + state => 'fixed - user', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Litter', + }, + category => 'Potholes', + counts => { + user => [0,0,2,2], + council => [0,0,2,2], + totals => [2,2,6,6], + }, + counts_after => { + user => [0,0,1,1], + council => [0,0,1,1], + totals => [1,1,3,3], + }, + report_counts => [2,4,0], + report_counts_after => [1,2,0], }, - counts_after => { - user => [0,0,0,0], - council => [0,0,1,1], - totals => [0,0,2,2], + { + desc => 'Limit display by ward', + p1 => { + state => 'confirmed', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Potholes', + # in real life it has commas around it and the search + # uses them + areas => ',20720,', + }, + p2 => { + state => 'fixed - council', + conf_dt => DateTime->now()->subtract( weeks => 1 ), + mark_dt => DateTime->now()->subtract( weeks => 1 ), + category => 'Litter', + areas => ',20720,', + }, + ward => 20720, + counts => { + user => [0,0,2,2], + council => [0,0,3,3], + totals => [2,2,8,8], + }, + counts_after => { + user => [0,0,0,0], + council => [0,0,1,1], + totals => [0,0,2,2], + }, + report_counts => [2,6,0], + report_counts_after => [0,2,0], }, - report_counts => [2,6,0], - report_counts_after => [0,2,0], - }, -) { - subtest $test->{desc} => sub { - make_problem( $test->{p1} ) if $test->{p1}; - make_problem( $test->{p2} ) if $test->{p2}; + ) { + subtest $test->{desc} => sub { + make_problem( $test->{p1} ) if $test->{p1}; + make_problem( $test->{p2} ) if $test->{p2}; - $mech->get_ok('/dashboard'); + $mech->get_ok('/dashboard'); - $res = $categories->scrape( $mech->content ); + $res = $categories->scrape( $mech->content ); - foreach my $row ( keys %{ $test->{counts} } ) { - check_row( $res, $row, $test->{counts}->{$row} ); - } + foreach my $row ( keys %{ $test->{counts} } ) { + check_row( $res, $row, $test->{counts}->{$row} ); + } - check_report_counts( $res, $test->{report_counts} ); + check_report_counts( $res, $test->{report_counts} ); - $mech->submit_form_ok( { - with_fields => { - category => $test->{category}, - ward => $test->{ward}, - } - } ); + $mech->submit_form_ok( { + with_fields => { + category => $test->{category}, + ward => $test->{ward}, + } + } ); - $res = $categories->scrape( $mech->content ); + $res = $categories->scrape( $mech->content ); - foreach my $row ( keys %{ $test->{counts_after} } ) { - check_row( $res, $row, $test->{counts_after}->{$row} ); - } - check_report_counts( $res, $test->{report_counts_after} ); - }; -} + foreach my $row ( keys %{ $test->{counts_after} } ) { + check_row( $res, $row, $test->{counts_after}->{$row} ); + } + check_report_counts( $res, $test->{report_counts_after} ); + }; + } -delete_problems(); + delete_problems(); -for my $test ( - { - desc => 'Selecting no state does nothing', - p1 => { - state => 'fixed - user', - conf_dt => DateTime->now(), - category => 'Potholes', + for my $test ( + { + desc => 'Selecting no state does nothing', + p1 => { + state => 'fixed - user', + conf_dt => DateTime->now(), + category => 'Potholes', + }, + p2 => { + state => 'confirmed', + conf_dt => DateTime->now(), + category => 'Litter', + }, + state => '', + report_counts => [2,0,0], + report_counts_after => [2,0,0], }, - p2 => { - state => 'confirmed', - conf_dt => DateTime->now(), - category => 'Litter', + { + desc => 'limit by state works', + state => 'fixed', + report_counts => [2,0,0], + report_counts_after => [1,0,0], }, - state => '', - report_counts => [2,0,0], - report_counts_after => [2,0,0], - }, - { - desc => 'limit by state works', - state => 'fixed', - report_counts => [2,0,0], - report_counts_after => [1,0,0], - }, - { - desc => 'All fixed states count as fixed', - p1 => { - state => 'fixed - council', - conf_dt => DateTime->now(), - category => 'Potholes', + { + desc => 'planned counted as action scheduled', + p1 => { + state => 'planned', + conf_dt => DateTime->now(), + category => 'Potholes', + }, + state => 'action scheduled', + report_counts => [3,0,0], + report_counts_after => [1,0,0], }, - p2 => { - state => 'fixed', - conf_dt => DateTime->now(), - category => 'Potholes', + { + desc => 'All fixed states count as fixed', + p1 => { + state => 'fixed - council', + conf_dt => DateTime->now(), + category => 'Potholes', + }, + p2 => { + state => 'fixed', + conf_dt => DateTime->now(), + category => 'Potholes', + }, + state => 'fixed', + report_counts => [5,0,0], + report_counts_after => [3,0,0], }, - state => 'fixed', - report_counts => [4,0,0], - report_counts_after => [3,0,0], - }, -) { - subtest $test->{desc} => sub { - make_problem( $test->{p1} ) if $test->{p1}; - make_problem( $test->{p2} ) if $test->{p2}; + ) { + subtest $test->{desc} => sub { + make_problem( $test->{p1} ) if $test->{p1}; + make_problem( $test->{p2} ) if $test->{p2}; - $mech->get_ok('/dashboard'); + $mech->get_ok('/dashboard'); - $res = $categories->scrape( $mech->content ); + $res = $categories->scrape( $mech->content ); - check_report_counts( $res, $test->{report_counts} ); + check_report_counts( $res, $test->{report_counts} ); - $mech->submit_form_ok( { - with_fields => { - state => $test->{state}, - } - } ); + $mech->submit_form_ok( { + with_fields => { + state => $test->{state}, + } + } ); - $res = $categories->scrape( $mech->content ); + $res = $categories->scrape( $mech->content ); - check_report_counts( $res, $test->{report_counts_after} ); + check_report_counts( $res, $test->{report_counts_after} ); + }; + } + + subtest 'export as csv' => sub { + make_problem( { + detail => "this report\nis split across\nseveral lines", + state => "confirmed", + conf_dt => DateTime->now(), + } ); + $mech->get_ok('/dashboard?export=1'); + open my $data_handle, '<', \$mech->content; + my $csv = Text::CSV->new( { binary => 1 } ); + my @rows; + while ( my $row = $csv->getline( $data_handle ) ) { + push @rows, $row; + } + is scalar @rows, 7, '1 (header) + 6 (reports) = 7 lines'; }; -} +}; +restore_time; sub make_problem { my $args = shift; @@ -573,12 +628,12 @@ sub make_problem { title => 'a problem', name => 'a user', anonymous => 1, - detail => 'some detail', + detail => $args->{detail} || 'some detail', state => $args->{state}, confirmed => $args->{conf_dt}, whensent => $args->{conf_dt}, lastupdate => $args->{mark_dt} || $args->{conf_dt}, - council => $test_council, + bodies_str => $body->id, postcode => 'EH99 1SP', latitude => '51', longitude => '1', @@ -632,10 +687,10 @@ sub check_report_counts { sub delete_problems { FixMyStreet::App->model('DB::Comment') - ->search( { 'problem.council' => $test_council }, { join => 'problem' } ) + ->search( { 'problem.bodies_str' => $body->id }, { join => 'problem' } ) ->delete; FixMyStreet::App->model('DB::Problem') - ->search( { council => $test_council } )->delete(); + ->search( { bodies_str => $body->id } )->delete(); } done_testing; diff --git a/t/app/controller/index.t b/t/app/controller/index.t index 462b21064..6b28a03d2 100644 --- a/t/app/controller/index.t +++ b/t/app/controller/index.t @@ -12,7 +12,6 @@ subtest "check that the form goes to /around" => sub { $mech->get_ok('/'); is $mech->uri->path, '/', "still on '/'"; - # submit form $mech->submit_form_ok( { with_fields => { pc => 'SW1A 1AA', } } ); # check that we are at /around @@ -47,7 +46,11 @@ subtest "does pc, (x,y), (e,n) or (lat,lon) go to /around" => sub { $uri->query_form( $test->{in} ); # get the uri and check for 302 - $mech->get_ok($uri); + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok($uri); + }; # check that we are at /around is $mech->uri->path, '/around', "Got to /around"; @@ -55,12 +58,10 @@ subtest "does pc, (x,y), (e,n) or (lat,lon) go to /around" => sub { } }; -$mech->delete_problems_for_council( 2651 ); - my $problem_rs = FixMyStreet::App->model('DB::Problem'); my $num = $problem_rs->count; -my @edinburgh_problems = $mech->create_problems_for_council(5, 2651, 'Front page'); +my @edinburgh_problems = $mech->create_problems_for_body(5, 2651, 'Front page'); is scalar @edinburgh_problems, 5, 'correct number of edinburgh problems created'; $mech->get_ok('/report/' . $edinburgh_problems[2]->id); @@ -74,4 +75,24 @@ ok $mech->get('/report/' . $edinburgh_problems[2]->id); is $mech->res->code, 403, 'page forbidden'; is $problem_rs->count, $num+5; -done_testing(); +my $oxon = $mech->create_body_ok(2237, 'Oxfordshire County Council', id => 2237); +subtest "prefilters /around if user has categories" => sub { + my $user = $mech->log_in_ok('test@example.com'); + my $categories = [ + $mech->create_contact_ok( body_id => $oxon->id, category => 'Cows', email => 'cows@example.net' )->id, + $mech->create_contact_ok( body_id => $oxon->id, category => 'Potholes', email => 'potholes@example.net' )->id, + ]; + $user->from_body($oxon); + $user->set_extra_metadata('categories', $categories); + $user->update; + + $mech->get_ok('/'); + # NB can't use visible_form_values because categories field is hidden + $mech->content_contains("Cows,Potholes"); +}; + +END { + $mech->delete_problems_for_body( 2651 ); + $mech->delete_body($oxon); + done_testing(); +} diff --git a/t/app/controller/json.t b/t/app/controller/json.t index 468fa5b31..b2cea674f 100644 --- a/t/app/controller/json.t +++ b/t/app/controller/json.t @@ -45,9 +45,11 @@ is_deeply # # put an entry in the database for this test my $user = $mech->create_user_ok('test@example.com'); +my $body = $mech->create_body_ok(2501, 'Wandsworth Borough Council'); + my $problem_args = { postcode => 'sw1a 1aa', - council => '2501', + bodies_str => $body->id, areas => ',105164,11806,11827,2247,2501,34817,42011,66045,70786,8519,', category => 'test category', title => 'Test title', @@ -86,7 +88,7 @@ is_deeply # 'category' => 'test category', 'confirmed' => '2000-01-01 12:01:00', 'lastupdate' => '2000-01-01 12:00:00', - 'council' => 'Wandsworth Borough Council', + 'bodies_str' => 'Wandsworth Borough Council', 'detail' => 'Test detail', 'id' => $problem->id, 'name' => 'Test Name', @@ -103,7 +105,7 @@ is_deeply # 'category' => 'test category', 'confirmed' => '2000-01-01 12:02:00', 'lastupdate' => '2000-01-01 12:00:00', - 'council' => 'Wandsworth Borough Council', + 'bodies_str' => 'Wandsworth Borough Council', 'detail' => 'Test detail', 'id' => $anon_problem->id, 'name' => '', diff --git a/t/app/controller/moderate.t b/t/app/controller/moderate.t new file mode 100644 index 000000000..10287ab22 --- /dev/null +++ b/t/app/controller/moderate.t @@ -0,0 +1,358 @@ +use strict; +use warnings; +use Test::More; +use utf8; + +use FixMyStreet::TestMech; +use FixMyStreet::App; +use Data::Dumper; + +my $mech = FixMyStreet::TestMech->new; +$mech->host('www.example.org'); + +my $BROMLEY_ID = 2482; +my $body = $mech->create_body_ok( $BROMLEY_ID, 'Bromley Council' ); + +my $dt = DateTime->now; + +my $user = $mech->create_user_ok('test-moderation@example.com', name => 'Test User'); +$user->user_body_permissions->delete_all; +$user->discard_changes; + +my $user2 = $mech->create_user_ok('test-moderation2@example.com', name => 'Test User 2'); + +sub create_report { + FixMyStreet::App->model('DB::Problem')->create( + { + postcode => 'BR1 3SB', + bodies_str => $body->id, + areas => ",$BROMLEY_ID,", + category => 'Other', + title => 'Good bad good', + detail => 'Good bad bad bad good bad', + used_map => 't', + name => 'Test User 2', + anonymous => 'f', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + lang => 'en-gb', + service => '', + cobrand => 'default', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.4129', + longitude => '0.007831', + user_id => $user2->id, + photo => $mech->get_photo_data, + }); +} +my $report = create_report(); +my $report2 = create_report(); + +my $REPORT_URL = '/report/' . $report->id ; + +subtest 'Auth' => sub { + + subtest 'Unaffiliated user cannot see moderation' => sub { + $mech->get_ok($REPORT_URL); + $mech->content_lacks('Moderat'); + + $mech->log_in_ok( $user->email ); + + $mech->get_ok($REPORT_URL); + $mech->content_lacks('Moderat'); + + $user->update({ from_body => $body->id }); + + $mech->get_ok($REPORT_URL); + $mech->content_lacks('Moderat'); + + $mech->get('/contact?m=1&id=' . $report->id); + is $mech->res->code, 400; + $mech->content_lacks('Good bad bad bad'); + }; + + subtest 'Affiliated and permissioned user can see moderation' => sub { + # login and from_body are done in previous test. + $user->user_body_permissions->create({ + body => $body, + permission_type => 'moderate', + }); + + $mech->get_ok($REPORT_URL); + $mech->content_contains('Moderat'); + }; +}; + +my %problem_prepopulated = ( + problem_show_name => 1, + problem_show_photo => 1, + problem_title => 'Good bad good', + problem_detail => 'Good bad bad bad good bad', +); + +subtest 'Problem moderation' => sub { + + subtest 'Post modify title and text' => sub { + $mech->get_ok($REPORT_URL); + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_title => 'Good good', + problem_detail => 'Good good improved', + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + $mech->content_like(qr/Moderated by Bromley Council/); + + $report->discard_changes; + is $report->title, 'Good [...] good'; + is $report->detail, 'Good [...] good [...]improved'; + }; + + subtest 'Revert title and text' => sub { + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_revert_title => 1, + problem_revert_detail => 1, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $report->discard_changes; + is $report->title, 'Good bad good'; + is $report->detail, 'Good bad bad bad good bad'; + }; + + subtest 'Make anonymous' => sub { + $mech->content_lacks('Reported anonymously'); + + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_show_name => 0, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $mech->content_contains('Reported anonymously'); + + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_show_name => 1, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $mech->content_lacks('Reported anonymously'); + }; + + subtest 'Hide photo' => sub { + $mech->content_contains('Photo of this report'); + + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_show_photo => 0, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $mech->content_lacks('Photo of this report'); + + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_show_photo => 1, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $mech->content_contains('Photo of this report'); + }; + + subtest 'Hide report' => sub { + $mech->clear_emails_ok; + + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_hide => 1, + }}); + $mech->base_unlike( qr{/report/}, 'redirected to front page' ); + + $report->discard_changes; + is $report->state, 'hidden', 'Is hidden'; + + my $email = $mech->get_email; + is $email->header('To'), '"Test User 2" <test-moderation2@example.com>', 'Sent to correct email'; + my $url = $mech->get_link_from_email($email); + ok $url, "extracted complain url '$url'"; + + $mech->get_ok($url); + $mech->content_contains('Good bad bad bad'); + + # reset + $report->update({ state => 'confirmed' }); + }; +}; + +$mech->content_lacks('Posted anonymously', 'sanity check'); + +subtest 'Problem 2' => sub { + my $REPORT2_URL = '/report/' . $report2->id ; + $mech->get_ok($REPORT2_URL); + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_title => 'Good good', + problem_detail => 'Good good improved', + }}); + $mech->base_like( qr{\Q$REPORT2_URL\E} ); + + $report2->discard_changes; + is $report2->title, 'Good [...] good'; + is $report2->detail, 'Good [...] good [...]improved'; + + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_revert_title => 1, + problem_revert_detail => 1, + }}); + $mech->base_like( qr{\Q$REPORT2_URL\E} ); + + $report2->discard_changes; + is $report2->title, 'Good bad good'; + is $report2->detail, 'Good bad bad bad good bad'; +}; + +sub create_update { + $report->comments->create({ + user => $user2, + name => 'Test User 2', + anonymous => 'f', + photo => $mech->get_photo_data, + text => 'update good good bad good', + state => 'confirmed', + mark_fixed => 0, + }); +} +my %update_prepopulated = ( + update_show_name => 1, + update_show_photo => 1, + update_detail => 'update good good bad good', +); + +my $update = create_update(); + +subtest 'updates' => sub { + + subtest 'Update modify text' => sub { + $mech->get_ok($REPORT_URL); + $mech->submit_form_ok({ with_fields => { + %update_prepopulated, + update_detail => 'update good good good', + }}) or die $mech->content; + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $update->discard_changes; + is $update->text, 'update good good [...] good', + }; + + subtest 'Revert text' => sub { + $mech->submit_form_ok({ with_fields => { + %update_prepopulated, + update_revert_detail => 1, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $update->discard_changes; + $update->discard_changes; + is $update->text, 'update good good bad good', + }; + + subtest 'Make anonymous' => sub { + $mech->content_lacks('Posted anonymously') + or die sprintf '%d (%d)', $update->id, $report->comments->count; + + $mech->submit_form_ok({ with_fields => { + %update_prepopulated, + update_show_name => 0, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $mech->content_contains('Posted anonymously'); + + $mech->submit_form_ok({ with_fields => { + %update_prepopulated, + update_show_name => 1, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $mech->content_lacks('Posted anonymously'); + }; + + subtest 'Hide photo' => sub { + $report->update({ photo => undef }); # hide the main photo so we can just look for text in comment + + $mech->get_ok($REPORT_URL); + + $mech->content_contains('Photo of this report') + or die $mech->content; + + $mech->submit_form_ok({ with_fields => { + %update_prepopulated, + update_show_photo => 0, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $mech->content_lacks('Photo of this report'); + + $mech->submit_form_ok({ with_fields => { + %update_prepopulated, + update_show_photo => 1, + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + + $mech->content_contains('Photo of this report'); + }; + + subtest 'Hide comment' => sub { + $mech->content_contains('update good good bad good'); + + $mech->submit_form_ok({ with_fields => { + %update_prepopulated, + update_hide => 1, + }}); + $mech->content_lacks('update good good bad good'); + }; + + $update->moderation_original_data->delete; +}; + +my $update2 = create_update(); + +subtest 'Update 2' => sub { + $mech->get_ok($REPORT_URL); + $mech->submit_form_ok({ with_fields => { + %update_prepopulated, + update_detail => 'update good good good', + }}) or die $mech->content; + + $update2->discard_changes; + is $update2->text, 'update good good [...] good', +}; + +subtest 'Now stop being a staff user' => sub { + $user->update({ from_body => undef }); + $mech->get_ok($REPORT_URL); + $mech->content_contains('Moderated by Bromley Council'); +}; + +subtest 'And do it as a superuser' => sub { + $user->update({ is_superuser => 1 }); + $mech->get_ok($REPORT_URL); + $mech->submit_form_ok({ with_fields => { + %problem_prepopulated, + problem_title => 'Good good', + problem_detail => 'Good good improved', + }}); + $mech->content_contains('Moderated by a FixMyStreet administrator'); +}; + +$update->delete; +$update2->delete; +$report->moderation_original_data->delete; +$report->delete; +$report2->delete; +$mech->delete_user($user); + +done_testing(); diff --git a/t/app/controller/my.t b/t/app/controller/my.t index da509e8ed..00070ed81 100644 --- a/t/app/controller/my.t +++ b/t/app/controller/my.t @@ -1,7 +1,7 @@ use strict; use warnings; -use Test::More tests => 11; +use Test::More; use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; @@ -9,11 +9,20 @@ my $mech = FixMyStreet::TestMech->new; $mech->get_ok('/my'); is $mech->uri->path, '/auth', "got sent to the sign in page"; -# sign in +$mech->create_problems_for_body(1, 1234, 'Test Title'); +my $other_user = FixMyStreet::DB->resultset('User')->find_or_create({ email => 'another@example.com' }); +$mech->create_problems_for_body(1, 1234, 'Another Title', { user => $other_user }); + my $user = $mech->log_in_ok( 'test@example.com' ); $mech->get_ok('/my'); -is $mech->uri->path, '/my', "stayed on '/my/' page"; +is $mech->uri->path, '/my', "stayed on '/my' page"; + +$mech->content_contains('Test Title'); +$mech->content_lacks('Another Title'); -# cleanup -$mech->delete_user( $user ); +done_testing(); +END { + $mech->delete_user($user); + $mech->delete_user($other_user); +} diff --git a/t/app/controller/my_planned.t b/t/app/controller/my_planned.t new file mode 100644 index 000000000..fa463e61e --- /dev/null +++ b/t/app/controller/my_planned.t @@ -0,0 +1,64 @@ +use strict; +use warnings; + +use Test::More; + +use FixMyStreet::TestMech; +my $mech = FixMyStreet::TestMech->new; + +$mech->get_ok('/my/planned'); +is $mech->uri->path, '/auth', "got sent to the sign in page"; + +my $body = $mech->create_body_ok(2237, 'Oxfordshire'); +my ($problem) = $mech->create_problems_for_body(1, $body->id, 'Test Title'); + +$mech->get_ok($problem->url); +$mech->content_lacks('Shortlist'); +$mech->content_lacks('Shortlisted'); + +my $user = $mech->log_in_ok( 'test@example.com' ); +$user->update({ from_body => $body }); +$user->user_body_permissions->find_or_create({ + body => $body, + permission_type => 'planned_reports', +}); + +$mech->get_ok('/my/planned'); +$mech->content_lacks('Test Title'); + +$user->add_to_planned_reports($problem); +$mech->get_ok('/my/planned'); +$mech->content_contains('Test Title'); + +$user->remove_from_planned_reports($problem); +$mech->get_ok('/my/planned'); +$mech->content_lacks('Test Title'); + +$user->add_to_planned_reports($problem); +$mech->get_ok('/my/planned'); +$mech->content_contains('Test Title'); + +$mech->get_ok($problem->url); +$mech->content_contains('Shortlisted'); +$mech->submit_form_ok({ with_fields => { 'shortlist-remove' => 1 } }); +$mech->content_contains('Shortlist'); +$mech->submit_form_ok({ with_fields => { 'shortlist-add' => 1 } }); +$mech->content_contains('Shortlisted'); + +$mech->get_ok('/my/planned?sort=shortlist&ajax=1'); +$mech->content_contains('shortlist-up'); +$mech->content_contains('shortlist-down'); + +$mech->get_ok('/my/planned?sort=created-desc&ajax=1'); +$mech->content_lacks('shortlist-up'); +$mech->content_lacks('shortlist-down'); + +$mech->get_ok('/my/planned?ajax=1'); +$mech->content_contains('shortlist-up'); +$mech->content_contains('shortlist-down'); + +done_testing(); + +END { + $mech->delete_user($user); +} diff --git a/t/app/controller/page_not_found.t b/t/app/controller/page_not_found.t index 05e983109..3c2bc3c3d 100644 --- a/t/app/controller/page_not_found.t +++ b/t/app/controller/page_not_found.t @@ -1,5 +1,3 @@ -#!/usr/bin/perl - use strict; use warnings; diff --git a/t/app/controller/photo.t b/t/app/controller/photo.t new file mode 100644 index 000000000..ad857b5e3 --- /dev/null +++ b/t/app/controller/photo.t @@ -0,0 +1,79 @@ +use strict; +use utf8; # sign in error message has – in it +use warnings; +use feature 'say'; +use Test::More; +use utf8; + +use FixMyStreet::TestMech; +use FixMyStreet::App; +use Web::Scraper; +use Path::Tiny; +use File::Temp 'tempdir'; + +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + +my $mech = FixMyStreet::TestMech->new; + +my $sample_file = path(__FILE__)->parent->child("sample.jpg"); +ok $sample_file->exists, "sample file $sample_file exists"; + +my $westminster = $mech->create_body_ok(2527, 'Liverpool City Council'); + +subtest "Check multiple upload worked" => sub { + $mech->get_ok('/around'); + + my $UPLOAD_DIR = tempdir( CLEANUP => 1 ); + + # submit initial pc form + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + UPLOAD_DIR => $UPLOAD_DIR, + }, sub { + + $mech->log_in_ok('test@example.com'); + + + # submit the main form + # can't post_ok as we lose the Content_Type header + # (TODO rewrite with HTTP::Request::Common and request_ok) + $mech->get_ok('/report/new?lat=53.4031156&lon=-2.9840579'); + my ($csrf) = $mech->content =~ /name="token" value="([^"]*)"/; + + $mech->post( '/report/new', + Content_Type => 'form-data', + Content => + { + submit_problem => 1, + token => $csrf, + title => 'Test', + lat => 53.4031156, lon => -2.9840579, # in Liverpool + pc => 'L1 4LN', + detail => 'Detail', + photo1 => [ $sample_file, undef, Content_Type => 'application/octet-stream' ], + photo2 => [ $sample_file, undef, Content_Type => 'application/octet-stream' ], + photo3 => [ $sample_file, undef, Content_Type => 'application/octet-stream' ], + name => 'Bob Jones', + may_show_name => '1', + email => 'test@example.com', + phone => '', + category => 'Street lighting', + } + ); + ok $mech->success, 'Made request with multiple photo upload'; + $mech->base_is('http://localhost/report/new'); + $mech->content_like( + qr[(<img align="right" src="/photo/temp.74e3362283b6ef0c48686fb0e161da4043bbcc97.jpeg" alt="">\s*){3}], + 'Three uploaded pictures are all shown, safe'); + $mech->content_contains( + 'name="upload_fileid" value="74e3362283b6ef0c48686fb0e161da4043bbcc97.jpeg,74e3362283b6ef0c48686fb0e161da4043bbcc97.jpeg,74e3362283b6ef0c48686fb0e161da4043bbcc97.jpeg"', + 'Returned upload_fileid contains expected hash, 3 times'); + my $image_file = path($UPLOAD_DIR, '74e3362283b6ef0c48686fb0e161da4043bbcc97.jpeg'); + ok $image_file->exists, 'File uploaded to temp'; + }; +}; + +done_testing(); diff --git a/t/app/controller/questionnaire.t b/t/app/controller/questionnaire.t index 3a6a3d6ad..f42908a3e 100644 --- a/t/app/controller/questionnaire.t +++ b/t/app/controller/questionnaire.t @@ -17,10 +17,7 @@ $mech->clear_emails_ok; # create a test user and report $mech->delete_user('test@example.com'); -my $user = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'test@example.com', name => 'Test User' } ); -ok $user, "created test user"; +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); my $dt = DateTime->now()->subtract( weeks => 5 ); my $report_time = $dt->ymd . ' ' . $dt->hms; @@ -30,7 +27,7 @@ my $sent_time = $sent->ymd . ' ' . $sent->hms; my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { postcode => 'EH1 1BB', - council => '2651', + bodies_str => '2651', areas => ',11808,135007,14419,134935,2651,20728,', category => 'Street lighting', title => 'Testing', @@ -61,12 +58,14 @@ FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( { } ); my $email = $mech->get_email; ok $email, "got an email"; -like $email->body, qr/fill in our short questionnaire/i, "got questionnaire email"; +my $plain = $mech->get_text_body_from_email($email, 1); +like $plain->body, qr/fill in our short questionnaire/i, "got questionnaire email"; -like $email->body, qr/Testing =96 Detail/, 'email contains encoded character'; -is $email->header('Content-Type'), 'text/plain; charset="windows-1252"', 'in the right character set'; +like $plain->body_str, qr/Testing \x{2013} Detail/, 'email contains encoded character'; +is $plain->header('Content-Type'), 'text/plain; charset="utf-8"', 'in the right character set'; -my ($token) = $email->body =~ m{http://.*?/Q/(\S+)}; +my $url = $mech->get_link_from_email($email); +my ($token) = $url =~ m{/Q/(\S+)}; ok $token, "extracted questionnaire token '$token'"; $mech->clear_emails_ok; @@ -87,17 +86,20 @@ foreach my $test ( { desc => 'User goes to questionnaire URL with a bad token', token_extra => 'BAD', - content => "we couldn't validate that token", + content => "Sorry, that wasn’t a valid link", + code => 400, }, { desc => 'User goes to questionnaire URL for a now-hidden problem', state => 'hidden', content => "we couldn't locate your problem", + code => 400, }, { desc => 'User goes to questionnaire URL for an already answered questionnaire', - answered => \'ms_current_timestamp()', + answered => \'current_timestamp', content => 'already answered this questionnaire', + code => 400, }, ) { subtest $test->{desc} => sub { @@ -107,7 +109,8 @@ foreach my $test ( $questionnaire->update; (my $token = $token->token); $token .= $test->{token_extra} if $test->{token_extra}; - $mech->get_ok("/Q/$token"); + $mech->get("/Q/$token"); + is $mech->res->code, $test->{code}, "Right status received"; $mech->content_contains( $test->{content} ); # Reset, no matter what test did $report->state( 'confirmed' ); @@ -191,6 +194,16 @@ foreach my $test ( }, }, { + desc => 'Fixed report, reopened, reported before, blank update, no further questionnaire', + problem_state => 'fixed', + fields => { + been_fixed => 'No', + reported => 'Yes', + another => 'No', + update => ' ', + }, + }, + { desc => 'Closed report, said fixed, reported before, no update, no further questionnaire', problem_state => 'closed', fields => { @@ -252,13 +265,13 @@ foreach my $test ( # Check the right HTML page has been returned $mech->content_like( qr/<title>[^<]*Questionnaire/m ); - $mech->content_contains( 'glad to hear it’s been fixed' ) + $mech->content_contains( 'Glad to hear' ) if $result =~ /fixed/; - $mech->content_lacks( 'glad to hear it’s been fixed' ) + $mech->content_lacks( 'Glad to hear' ) if $result !~ /fixed/; $mech->content_contains( 'get some more information about the status of your problem' ) if $result eq 'unknown'; - $mech->content_contains( "sorry to hear that" ) + $mech->content_contains( "sorry to hear" ) if $result eq 'confirmed' || $result eq 'closed'; # Check the database has the right information @@ -266,7 +279,7 @@ foreach my $test ( $questionnaire->discard_changes; is $report->state, $result eq 'unknown' ? $test->{problem_state} : $result; is $report->send_questionnaire, $another; - ok DateTime::Format::Pg->format_datetime( $report->lastupdate) gt $report_time, 'lastupdate changed' + ok (DateTime::Format::Pg->format_datetime( $report->lastupdate) gt $report_time, 'lastupdate changed') unless $test->{fields}{been_fixed} eq 'Unknown' || $test->{lastupdate_static}; is $questionnaire->old_state, $test->{problem_state}; is $questionnaire->new_state, $result; @@ -315,11 +328,11 @@ my $comment = FixMyStreet::App->model('DB::Comment')->find_or_create( ); subtest 'Check updates are shown correctly on questionnaire page' => sub { $mech->get_ok("/Q/" . $token->token); - $mech->content_contains( 'updates that have been left' ); + $mech->content_contains( 'Show all updates' ); $mech->content_contains( 'This is some update text' ); }; -for my $test ( +for my $test ( { state => 'confirmed', fixed => 0 @@ -329,6 +342,10 @@ for my $test ( fixed => 0 }, { + state => 'action scheduled', + fixed => 0 + }, + { state => 'in progress', fixed => 0 }, @@ -337,6 +354,18 @@ for my $test ( fixed => 0 }, { + state => 'duplicate', + fixed => 0 + }, + { + state => 'not responsible', + fixed => 0 + }, + { + state => 'unable to fix', + fixed => 0 + }, + { state => 'closed', fixed => 0 }, @@ -367,32 +396,25 @@ for my $test ( }; } -SKIP: { - skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 18 ) - unless FixMyStreet::Cobrand->exists('emptyhomes'); - - # EHA extra checking - ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes'; - - # Reset, and all the questionaire sending function - FIXME should it detect site itself somehow? +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], +}, sub { + $report->discard_changes; $report->send_questionnaire( 1 ); $report->update; $questionnaire->delete; - FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( { - site => 'emptyhomes' - } ); - $email = $mech->get_email; - ok $email, "got an email"; - $mech->clear_emails_ok; + FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires(); - like $email->body, qr/fill in this short questionnaire/i, "got questionnaire email"; - ($token) = $email->body =~ m{http://.*?/Q/(\S+)}; + my $email = $mech->get_email; + my $body = $mech->get_text_body_from_email($email); + $mech->clear_emails_ok; + $body =~ s/\s+/ /g; + like $body, qr/fill in our short questionnaire/i, "got questionnaire email"; + my $url = $mech->get_link_from_email($email); + ($token) = $url =~ m{/Q/(\S+)}; ok $token, "extracted questionnaire token '$token'"; - $mech->get_ok("/Q/" . $token); - $mech->content_contains( 'should have reported what they have done' ); - # Test already answered the ever reported question, so not shown again $dt = $dt->add( weeks => 4 ); my $questionnaire2 = FixMyStreet::App->model('DB::Questionnaire')->find_or_create( @@ -403,43 +425,38 @@ SKIP: { } ); ok $questionnaire2, 'added another questionnaire'; - ok $mech->host("fixmystreet.com"), 'change host to fixmystreet'; $mech->get_ok("/Q/" . $token); $mech->title_like( qr/Questionnaire/ ); $mech->content_contains( 'Has this problem been fixed?' ); $mech->content_lacks( 'ever reported' ); - # EHA extra checking - ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes'; - $mech->get_ok("/Q/" . $token); - $mech->content_contains( 'made a lot of progress' ); - $token = FixMyStreet::App->model("DB::Token")->find( { scope => 'questionnaire', token => $token } ); ok $token, 'found token for questionnaire'; $questionnaire = FixMyStreet::App->model('DB::Questionnaire')->find( { id => $token->data } ); ok $questionnaire, 'found questionnaire'; $questionnaire2->delete; -} - -SKIP: { - skip( "Need 'fiksgatami' in ALLOWED_COBRANDS config", 5 ) - unless FixMyStreet::Cobrand->exists('fiksgatami'); +}; +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fiksgatami' ], +}, sub { # I18N Unicode extra testing using FiksGataMi + $report->discard_changes; $report->send_questionnaire( 1 ); $report->cobrand( 'fiksgatami' ); $report->update; $questionnaire->delete; - FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( { site => 'fixmystreet' } ); # It's either fixmystreet or emptyhomes + FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires(); $email = $mech->get_email; ok $email, "got an email"; $mech->clear_emails_ok; - like $email->body, qr/Testing =96 Detail/, 'email contains encoded character from user'; - like $email->body, qr/sak p=E5 FiksGataMi/, 'email contains encoded character from template'; - is $email->header('Content-Type'), 'text/plain; charset="windows-1252"', 'email is in right encoding'; -} + my $plain = $mech->get_text_body_from_email($email, 1); + like $plain->body_str, qr/Testing \x{2013} Detail/, 'email contains encoded character from user'; + like $plain->body_str, qr/sak p\xe5 FiksGataMi/, 'email contains encoded character from template'; + is $plain->header('Content-Type'), 'text/plain; charset="utf-8"', 'email is in right encoding'; +}; $mech->delete_user('test@example.com'); done_testing(); diff --git a/t/app/controller/report_as_other.t b/t/app/controller/report_as_other.t new file mode 100644 index 000000000..551a59481 --- /dev/null +++ b/t/app/controller/report_as_other.t @@ -0,0 +1,190 @@ +use strict; +use warnings; +use Test::More; + +use FixMyStreet::TestMech; +use FixMyStreet::App; + +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + +my $mech = FixMyStreet::TestMech->new; + +my $body = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $contact1 = $mech->create_contact_ok( body_id => $body->id, category => 'Street lighting', email => 'highways@example.com' ); +my $contact2 = $mech->create_contact_ok( body_id => $body->id, category => 'Potholes', email => 'potholes@example.com' ); + +my $test_email = 'body-user@example.net'; +my $user = $mech->log_in_ok($test_email); +$user->update({ from_body => $body->id, name => 'Body User' }); + +my ($report_to_update) = $mech->create_problems_for_body(1, $body->id, 'Title'); + +subtest "Body user, no permissions, no special reporting tools shown" => sub { + start_report(); + dropdown_shown(0); + start_update(); + dropdown_shown(0, 'updateForm'); +}; + +subtest "Body user, has permission to add report as council" => sub { + my $report = add_report( + 'contribute_as_body', + form_as => 'body', + title => "Test Report", + detail => 'Test report details.', + category => 'Street lighting', + ); + is $report->name, 'Oxfordshire County Council', 'report name is body'; + is $report->user->name, 'Body User', 'user name unchanged'; + is $report->user->id, $user->id, 'user matches'; + is $report->anonymous, 0, 'report not anonymous'; +}; + +my @users; +subtest "Body user, has permission to add report as another user" => sub { + my $report = add_report( + 'contribute_as_another_user', + form_as => 'another_user', + title => "Test Report", + detail => 'Test report details.', + category => 'Potholes', + name => 'Another User', + email => 'another@example.net', + ); + is $report->name, 'Another User', 'report name is given name'; + is $report->user->name, 'Another User', 'user name matches'; + is $report->user->email, 'another@example.net', 'user email correct'; + isnt $report->user->id, $user->id, 'user does not match'; + like $mech->get_text_body_from_email, qr/Your report to Oxfordshire County Council has been logged/; + push @users, $report->user; +}; + +subtest "Body user, has permission to add report as another (existing) user" => sub { + $mech->create_user_ok('existing@example.net', name => 'Existing User'); + my $report = add_report( + 'contribute_as_another_user', + form_as => 'another_user', + title => "Test Report", + detail => 'Test report details.', + category => 'Potholes', + name => 'Existing Yooser', + email => 'existing@example.net', + ); + is $report->name, 'Existing Yooser', 'report name is given name'; + is $report->user->name, 'Existing User', 'user name remains same'; + is $report->user->email, 'existing@example.net', 'user email correct'; + isnt $report->user->id, $user->id, 'user does not match'; + like $mech->get_text_body_from_email, qr/Your report to Oxfordshire County Council has been logged/; + push @users, $report->user; +}; + +subtest "Body user, has permission to add update as council" => sub { + my $update = add_update( + 'contribute_as_body', + form_as => 'body', + update => 'Test Update', + ); + is $update->name, 'Oxfordshire County Council', 'update name is body'; + is $update->user->name, 'Body User', 'user name unchanged'; + is $update->user->id, $user->id, 'user matches'; + is $update->anonymous, 0, 'update not anonymous'; +}; + +subtest "Body user, has permission to add update as another user" => sub { + my $update = add_update( + 'contribute_as_another_user', + form_as => 'another_user', + update => 'Test Update', + name => 'Another User', + rznvy => 'another2@example.net', + ); + is $update->name, 'Another User', 'update name is given name'; + is $update->user->name, 'Another User', 'user name matches'; + is $update->user->email, 'another2@example.net', 'user email correct'; + isnt $update->user->id, $user->id, 'user does not match'; + like $mech->get_text_body_from_email, qr/Your update has been logged/; + push @users, $update->user; +}; + +subtest "Body user, has permission to add update as another (existing) user" => sub { + my $update = add_update( + 'contribute_as_another_user', + form_as => 'another_user', + update => 'Test Update', + name => 'Existing Yooser', + rznvy => 'existing@example.net', + ); + is $update->name, 'Existing Yooser', 'update name is given name'; + is $update->user->name, 'Existing User', 'user name remains same'; + is $update->user->email, 'existing@example.net', 'user email correct'; + isnt $update->user->id, $user->id, 'user does not match'; + like $mech->get_text_body_from_email, qr/Your update has been logged/; +}; + +done_testing(); + +END { + $mech->delete_body($body); + $mech->delete_user($_) for @users; +} + +sub start_report { + my $permission = shift; + $_->delete for $user->user_body_permissions; + $user->user_body_permissions->create({ body => $body, permission_type => $permission }) + if $permission; + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/report/new?latitude=51.7549262252&longitude=-1.25617899435'); + }; +} + +sub add_report { + my ($permission, %fields) = @_; + start_report($permission); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + dropdown_shown(1); + $mech->submit_form_ok({ + with_fields => \%fields, + }, "submit details"); + }; + $mech->content_contains('Thank you for reporting this issue'); + my $report = FixMyStreet::DB->resultset("Problem")->search(undef, { order_by => { -desc => 'id' } })->first; + ok $report, "Found the report"; + is $report->state, 'confirmed', "report is now confirmed"; + return $report; +} + +sub start_update { + my $permission = shift; + $_->delete for $user->user_body_permissions; + $user->user_body_permissions->create({ body => $body, permission_type => $permission }) + if $permission; + $mech->get_ok('/report/' . $report_to_update->id); +} + +sub add_update { + my ($permission, %fields) = @_; + start_update($permission); + dropdown_shown(1, 'updateForm'); + $mech->submit_form_ok({ + with_fields => \%fields, + }, "submit details"); + $mech->content_contains('Thank you for updating this issue'); + my $update = FixMyStreet::DB->resultset("Comment")->search(undef, { order_by => { -desc => 'id' } })->first; + ok $update, "Found the update"; + is $update->state, 'confirmed', "update is now confirmed"; + return $update; +} + +sub dropdown_shown { + my ($shown, $name) = @_; + is grep({ $_ eq 'form_as' } keys %{$mech->visible_form_values($name)}), $shown, "Dropdown shown = $shown"; +} diff --git a/t/app/controller/report_display.t b/t/app/controller/report_display.t index 39f1b59a7..b35a4a026 100644 --- a/t/app/controller/report_display.t +++ b/t/app/controller/report_display.t @@ -5,21 +5,16 @@ use Test::More; use FixMyStreet::TestMech; use Web::Scraper; use Path::Class; +use Test::LongString; use DateTime; my $mech = FixMyStreet::TestMech->new; # create a test user and report $mech->delete_user('test@example.com'); -my $user = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'test@example.com', name => 'Test User' } ); -ok $user, "created test user"; +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); -my $user2 = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'test2@example.com', name => 'Other User' } ); -ok $user2, "created test user"; +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Other User'); my $dt = DateTime->new( year => 2011, @@ -30,31 +25,16 @@ my $dt = DateTime->new( second => 23 ); -my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( - { - postcode => 'SW1A 1AA', - council => '2504', - areas => ',105255,11806,11828,2247,2504,', - category => 'Other', - title => 'Test 2', - detail => 'Test 2 Detail', - used_map => 't', - name => 'Test User', - anonymous => 'f', - state => 'confirmed', - confirmed => $dt->ymd . ' ' . $dt->hms, - lang => 'en-gb', - service => '', - cobrand => 'default', - cobrand_data => '', - send_questionnaire => 't', - latitude => '51.5016605453401', - longitude => '-0.142497580865087', - user_id => $user->id, - } -); +my $westminster = $mech->create_body_ok(2504, 'Westminster City Council'); +my ($report, $report2) = $mech->create_problems_for_body(2, $westminster->id, "Example", { + user => $user, + confirmed => $dt->ymd . ' ' . $dt->hms, +}); +$report->update({ + title => 'Test 2', + detail => 'Test 2 Detail' +}); my $report_id = $report->id; -ok $report, "created test report - $report_id"; subtest "check that no id redirects to homepage" => sub { $mech->get_ok('/report'); @@ -94,6 +74,7 @@ subtest "change report to unconfirmed and check for 404 status" => sub { ok $report->update( { state => 'confirmed' } ), 'confirm report again'; }; + subtest "change report to hidden and check for 410 status" => sub { ok $report->update( { state => 'hidden' } ), 'hide report'; ok $mech->get("/report/$report_id"), "get '/report/$report_id'"; @@ -129,6 +110,22 @@ subtest "check owner of report can view non public reports" => sub { ok $report->update( { non_public => 0 } ), 'make report public'; }; +subtest "duplicate reports are signposted correctly" => sub { + $report2->set_extra_metadata(duplicate_of => $report->id); + $report2->state('duplicate'); + $report2->update; + + my $report2_id = $report2->id; + ok $mech->get("/report/$report2_id"), "get '/report/$report2_id'"; + $mech->content_contains('This report is a duplicate'); + $mech->content_contains($report->title); + $mech->log_out_ok; + + $report2->unset_extra_metadata('duplicate_of'); + $report2->state('confirmed'); + $report2->update; +}; + subtest "test a good report" => sub { $mech->get_ok("/report/$report_id"); is $mech->uri->path, "/report/$report_id", "at /report/$report_id"; @@ -169,14 +166,14 @@ foreach my $meta ( category => '', service => 'Transport service', meta => -'Reported by Transport service by Test User at 15:47, Sat 16 April 2011' +'Reported via Transport service by Test User at 15:47, Sat 16 April 2011' }, { anonymous => 'f', category => 'Roads', service => 'Transport service', meta => -'Reported by Transport service in the Roads category by Test User at 15:47, Sat 16 April 2011' +'Reported via Transport service in the Roads category by Test User at 15:47, Sat 16 April 2011' }, { anonymous => 't', @@ -196,14 +193,14 @@ foreach my $meta ( category => '', service => 'Transport service', meta => -'Reported by Transport service anonymously at 15:47, Sat 16 April 2011' +'Reported via Transport service anonymously at 15:47, Sat 16 April 2011' }, { anonymous => 't', category => 'Roads', service => 'Transport service', meta => -'Reported by Transport service in the Roads category anonymously at 15:47, Sat 16 April 2011' +'Reported via Transport service in the Roads category anonymously at 15:47, Sat 16 April 2011' }, ) { @@ -213,13 +210,13 @@ foreach my $meta ( $report->update; subtest "test correct problem meta information" => sub { $mech->get_ok("/report/$report_id"); - + is $mech->extract_problem_meta, $meta->{meta}; }; } -for my $test ( +for my $test ( { description => 'new report', date => DateTime->now, @@ -283,6 +280,38 @@ for my $test ( fixed => 1 }, { + description => 'duplicate report', + date => DateTime->now, + state => 'duplicate', + banner_id => 'closed', + banner_text => 'closed', + fixed => 0 + }, + { + description => 'not responsible report', + date => DateTime->now, + state => 'not responsible', + banner_id => 'closed', + banner_text => 'closed', + fixed => 0 + }, + { + description => 'unable to fix report', + date => DateTime->now, + state => 'unable to fix', + banner_id => 'closed', + banner_text => 'closed', + fixed => 0 + }, + { + description => 'internal referral report', + date => DateTime->now, + state => 'internal referral', + banner_id => 'closed', + banner_text => 'closed', + fixed => 0 + }, + { description => 'closed report', date => DateTime->now, state => 'closed', @@ -299,6 +328,14 @@ for my $test ( fixed => 0 }, { + description => 'action scheduled report', + date => DateTime->now, + state => 'action scheduled', + banner_id => 'progress', + banner_text => 'progress', + fixed => 0 + }, + { description => 'planned report', date => DateTime->now, state => 'planned', @@ -307,7 +344,7 @@ for my $test ( fixed => 0 }, { - description => 'in progressreport', + description => 'in progress report', date => DateTime->now, state => 'in progress', banner_id => 'progress', @@ -345,30 +382,33 @@ for my $test ( }; } -for my $test ( +my $body_westminster = $mech->create_body_ok(2504, 'Westminster City Council'); +my $body_camden = $mech->create_body_ok(2505, 'Camden Borough Council'); + +for my $test ( { desc => 'no state dropdown if user not from authority', - from_body => 0, + from_body => undef, no_state => 1, - report_council => '2504', + report_body => $body_westminster->id, }, { desc => 'state dropdown if user from authority', - from_body => 2504, + from_body => $body_westminster->id, no_state => 0, - report_council => '2504', + report_body => $body_westminster->id, }, { - desc => 'no state dropdown if user not from same council as problem', - from_body => 2505, + desc => 'no state dropdown if user not from same body as problem', + from_body => $body_camden->id, no_state => 1, - report_council => '2504', + report_body => $body_westminster->id, }, { - desc => 'state dropdown if user from authority and problem sent to multiple councils', - from_body => 2504, + desc => 'state dropdown if user from authority and problem sent to multiple bodies', + from_body => $body_westminster->id, no_state => 0, - report_council => '2504,2506', + report_body => $body_westminster->id . ',2506', }, ) { subtest $test->{desc} => sub { @@ -377,7 +417,7 @@ for my $test ( $user->update; $report->discard_changes; - $report->council( $test->{report_council} ); + $report->bodies_str( $test->{report_body} ); $report->update; $mech->get_ok("/report/$report_id"); @@ -390,10 +430,160 @@ for my $test ( }; } -$report->discard_changes; -$report->council( 2504 ); -$report->update; +subtest "Zurich unconfirmeds are 200" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + MAP_TYPE => 'Zurich,OSM', + }, sub { + $mech->host( 'zurich.example.com' ); + ok $report->update( { state => 'unconfirmed' } ), 'unconfirm report'; + $mech->get_ok("/report/$report_id"); + $mech->content_contains( 'Überprüfung ausstehend' ); + ok $report->update( { state => 'confirmed' } ), 'confirm report again'; + $mech->host( 'www.fixmystreet.com' ); + }; +}; + +subtest "Zurich banners are displayed correctly" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + MAP_TYPE => 'Zurich,OSM', + }, sub { + $mech->host( 'zurich.example.com' ); -# tidy up -$mech->delete_user('test@example.com'); -done_testing(); + for my $test ( + { + description => 'new report', + state => 'unconfirmed', + banner_id => 'closed', + banner_text => 'Erfasst' + }, + { + description => 'confirmed report', + state => 'confirmed', + banner_id => 'closed', + banner_text => 'Aufgenommen', + }, + { + description => 'fixed report', + state => 'fixed - council', + banner_id => 'fixed', + banner_text => 'Beantwortet', + }, + { + description => 'closed report', + state => 'closed', + banner_id => 'closed', + banner_text => _('Extern'), + }, + { + description => 'in progress report', + state => 'in progress', + banner_id => 'progress', + banner_text => 'In Bearbeitung', + }, + { + description => 'planned report', + state => 'planned', + banner_id => 'progress', + banner_text => 'In Bearbeitung', + }, + { + description => 'planned report', + state => 'planned', + banner_id => 'progress', + banner_text => 'In Bearbeitung', + }, + { + description => 'jurisdiction unknown', + state => 'unable to fix', + banner_id => 'fixed', + # We can't use _('Jurisdiction Unknown') here because + # TestMech::extract_problem_banner decodes the HTML entities before + # the string is passed back. + banner_text => 'Zust\x{e4}ndigkeit unbekannt', + }, + ) { + subtest "banner for $test->{description}" => sub { + $report->state( $test->{state} ); + $report->update; + + $mech->get_ok("/report/$report_id"); + is $mech->uri->path, "/report/$report_id", "at /report/$report_id"; + my $banner = $mech->extract_problem_banner; + if ( $banner->{text} ) { + $banner->{text} =~ s/^ //g; + $banner->{text} =~ s/ $//g; + } + + is $banner->{id}, $test->{banner_id}, 'banner id'; + if ($test->{banner_text}) { + like_string( $banner->{text}, qr/$test->{banner_text}/i, 'banner text is ' . $test->{banner_text} ); + } else { + is $banner->{text}, $test->{banner_text}, 'banner text'; + } + + }; + } + + $mech->host( 'www.fixmystreet.com' ); + }; +}; + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council', id => 2237); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); + +subtest "check user details show when a user has correct permissions" => sub { + $report->update( { + name => 'Oxfordshire County Council', + user_id => $oxfordshireuser->id, + service => '', + anonymous => 'f', + bodies_str => $oxfordshire->id, + confirmed => '2012-01-10 15:17:00' + }); + + ok $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'view_body_contribute_details', + }); + + $mech->log_in_ok( $oxfordshireuser->email ); + ok $mech->get("/report/$report_id"), "get '/report/$report_id'"; + is $mech->extract_problem_meta, + 'Reported in the Roads category by Oxfordshire County Council (Council User) at 15:17, Tue 10 January 2012', + 'correct problem meta information'; + + ok $oxfordshireuser->user_body_permissions->delete_all, "Remove view_body_contribute_details permissions"; + + ok $mech->get("/report/$report_id"), "get '/report/$report_id'"; + is $mech->extract_problem_meta, + 'Reported in the Roads category by Oxfordshire County Council at 15:17, Tue 10 January 2012', + 'correct problem meta information for user without relevant permissions'; + + $mech->log_out_ok; + + ok $mech->get("/report/$report_id"), "get '/report/$report_id'"; + is $mech->extract_problem_meta, + 'Reported in the Roads category by Oxfordshire County Council at 15:17, Tue 10 January 2012', + 'correct problem meta information for logged out user'; + +}; + +subtest "check brackets don't appear when username and report name are the same" => sub { + $report->update( { + name => 'Council User' + }); + + $mech->log_in_ok( $oxfordshireuser->email ); + ok $mech->get("/report/$report_id"), "get '/report/$report_id'"; + is $mech->extract_problem_meta, + 'Reported in the Roads category by Council User at 15:17, Tue 10 January 2012', + 'correct problem meta information'; +}; + +END { + $mech->delete_user('test@example.com'); + $mech->delete_body($westminster); + done_testing(); +} diff --git a/t/app/controller/report_import.t b/t/app/controller/report_import.t index eb686b44e..b956b61ae 100644 --- a/t/app/controller/report_import.t +++ b/t/app/controller/report_import.t @@ -3,8 +3,13 @@ use warnings; use Test::More; use FixMyStreet::TestMech; +use FixMyStreet::App; use Web::Scraper; use Path::Class; +use LWP::Protocol::PSGI; +use t::Mock::MapItZurich; + +LWP::Protocol::PSGI->register(t::Mock::MapItZurich->to_psgi_app, host => 'mapit.zurich'); my $mech = FixMyStreet::TestMech->new; $mech->get_ok('/import'); @@ -12,6 +17,22 @@ $mech->get_ok('/import'); my $sample_file = file(__FILE__)->parent->file("sample.jpg")->stringify; ok -e $sample_file, "sample file $sample_file exists"; +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + +my $body = $mech->create_body_ok(2245, 'Wiltshire Council'); +$mech->create_contact_ok( + body_id => $body->id, + category => 'Street lighting', + email => 'streetlighting@example.com', +); +$mech->create_contact_ok( + body_id => $body->id, + category => 'Potholes', + email => 'highways@example.com', +); + # submit an empty report to import - check we get all errors subtest "Test creating bad partial entries" => sub { @@ -58,10 +79,14 @@ subtest "Test creating bad partial entries" => sub { { $mech->get_ok('/import'); - $mech->submit_form_ok( # - { with_fields => $test->{fields} }, - "fill in form" - ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + }, sub { + $mech->submit_form_ok( # + { with_fields => $test->{fields} }, + "fill in form" + ); + }; is_deeply( $mech->import_errors, $test->{errors}, "expected errors" ); } @@ -69,7 +94,6 @@ subtest "Test creating bad partial entries" => sub { }; subtest "Submit a correct entry" => sub { - $mech->get_ok('/import'); $mech->submit_form_ok( # @@ -90,15 +114,16 @@ subtest "Submit a correct entry" => sub { is $mech->content, 'SUCCESS', "Got success response"; # check that we have received the email - $mech->email_count_is(1); - my $email = $mech->get_email; + my $token_url = $mech->get_link_from_email; $mech->clear_emails_ok; - - my ($token_url) = $email->body =~ m{(http://\S+)}; ok $token_url, "Found a token url $token_url"; # go to the token url - $mech->get_ok($token_url); + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok($token_url); + }; # check that we are on '/around' is $mech->uri->path, '/around', "sent to /around"; @@ -107,10 +132,15 @@ subtest "Submit a correct entry" => sub { is_deeply $mech->visible_form_values, { pc => '' }, "check only pc field is shown"; - $mech->submit_form_ok( # - { with_fields => { pc => 'SW1A 1AA' } }, - "fill in postcode" - ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( + { with_fields => { pc => 'SN15 5NG' } }, + "fill in postcode" + ); + }; is $mech->uri->path, '/report/new', "sent to report page"; @@ -120,7 +150,9 @@ subtest "Submit a correct entry" => sub { name => 'Test User', title => 'Test report', detail => 'This is a test report', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', phone => '', may_show_name => '1', category => '-- Pick a category --', @@ -129,19 +161,24 @@ subtest "Submit a correct entry" => sub { # Check photo present, and still there after map submission (testing bug #18) $mech->content_contains( '<img align="right" src="/photo/' ); - $mech->content_contains('latitude" value="51.50101"', 'Check latitude'); - $mech->content_contains('longitude" value="-0.141587"', 'Check longitude'); - $mech->submit_form_ok( - { - button => 'tile_32742.21793', - x => 10, - y => 10, - }, - "New map location" - ); + $mech->content_contains('latitude" value="51.5"', 'Check latitude'); + $mech->content_contains('longitude" value="-2.1"', 'Check longitude'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( + { + button => 'tile_16192.10896', + x => 10, + y => 10, + }, + "New map location" + ); + }; $mech->content_contains( '<img align="right" src="/photo/' ); - $mech->content_contains('latitude" value="51.50519"', 'Check latitude'); - $mech->content_contains('longitude" value="-0.142608"', 'Check longitude'); + $mech->content_contains('latitude" value="51.508475"', 'Check latitude'); + $mech->content_contains('longitude" value="-2.108946"', 'Check longitude'); # check that fields haven't changed at all is_deeply $mech->visible_form_values, @@ -149,7 +186,9 @@ subtest "Submit a correct entry" => sub { name => 'Test User', title => 'Test report', detail => 'This is a test report', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', phone => '', may_show_name => '1', category => '-- Pick a category --', @@ -157,19 +196,24 @@ subtest "Submit a correct entry" => sub { "check imported fields are shown"; # change the details - $mech->submit_form_ok( # - { - with_fields => { - name => 'New Test User', - title => 'New Test report', - detail => 'This is a test report', - phone => '01234 567 890', - may_show_name => '1', - category => 'Street lighting', - } - }, - "Update details and save" - ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( + { + with_fields => { + name => 'New Test User', + title => 'New Test report', + detail => 'This is a test report', + phone => '01234 567 890', + may_show_name => '1', + category => 'Street lighting', + } + }, + "Update details and save" + ); + }; # check that report has been created my $user = @@ -192,8 +236,8 @@ subtest "Submit a correct entry (with location)" => sub { { with_fields => { service => 'test-script', - lat => '51.5010096115539', # SW1A 1AA - lon => '-0.141587067110009', + lat => '51.5', + lon => '-2.1', name => 'Test User ll', email => 'test-ll@example.com', subject => 'Test report ll', @@ -208,15 +252,17 @@ subtest "Submit a correct entry (with location)" => sub { is $mech->content, 'SUCCESS', "Got success response"; # check that we have received the email - $mech->email_count_is(1); - my $email = $mech->get_email; + my $token_url = $mech->get_link_from_email; $mech->clear_emails_ok; - - my ($token_url) = $email->body =~ m{(http://\S+)}; ok $token_url, "Found a token url $token_url"; # go to the token url - $mech->get_ok($token_url); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok($token_url); + }; # check that we are on '/report/new' is $mech->uri->path, '/report/new', "sent to /report/new"; @@ -227,7 +273,9 @@ subtest "Submit a correct entry (with location)" => sub { name => 'Test User ll', title => 'Test report ll', detail => 'This is a test report ll', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', phone => '', may_show_name => '1', category => '-- Pick a category --', @@ -235,19 +283,24 @@ subtest "Submit a correct entry (with location)" => sub { "check imported fields are shown"; # change the details - $mech->submit_form_ok( # - { - with_fields => { - name => 'New Test User ll', - title => 'New Test report ll', - detail => 'This is a test report ll', - phone => '01234 567 890', - may_show_name => '1', - category => 'Street lighting', - } - }, - "Update details and save" - ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( # + { + with_fields => { + name => 'New Test User ll', + title => 'New Test report ll', + detail => 'This is a test report ll', + phone => '01234 567 890', + may_show_name => '1', + category => 'Street lighting', + } + }, + "Update details and save" + ); + }; # check that report has been created my $user = @@ -263,72 +316,77 @@ subtest "Submit a correct entry (with location)" => sub { }; subtest "Submit a correct entry (with location) to cobrand" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + MAPIT_URL => 'http://mapit.zurich/', + MAPIT_TYPES => [ 'O08' ], + MAPIT_ID_WHITELIST => [], + MAP_TYPE => 'Zurich,OSM', + }, sub { + ok $mech->host("zurich.example.org"), 'change host to zurich'; - SKIP: { - skip( "Need 'fiksgatami' in ALLOWED_COBRANDS config", 20 ) - unless FixMyStreet::Cobrand->exists('fiksgatami'); - mySociety::MaPit::configure('http://mapit.nuug.no/'); - ok $mech->host("fiksgatami.no"), 'change host to fiksgatami'; + $mech->get_ok('/import'); - $mech->get_ok('/import'); + $mech->submit_form_ok( # + { + with_fields => { + service => 'test-script', + lat => '47.4', + lon => '8.5', + name => 'Test User ll', + email => 'test-ll@example.com', + subject => 'Test report ll', + detail => 'This is a test report ll', + photo => $sample_file, + } + }, + "fill in form" + ); - $mech->submit_form_ok( # - { - with_fields => { - service => 'test-script', - lat => '59', - lon => '10', - name => 'Test User ll', - email => 'test-ll@example.com', - subject => 'Test report ll', - detail => 'This is a test report ll', - photo => $sample_file, - } - }, - "fill in form" - ); + is_deeply( $mech->import_errors, [], "got no errors" ); + is $mech->content, 'SUCCESS', "Got success response"; - is_deeply( $mech->import_errors, [], "got no errors" ); - is $mech->content, 'SUCCESS', "Got success response"; + # check that we have received the email + my $token_url = $mech->get_link_from_email; + $mech->clear_emails_ok; + ok $token_url, "Found a token url $token_url"; - # check that we have received the email - $mech->email_count_is(1); - my $email = $mech->get_email; - $mech->clear_emails_ok; + # go to the token url + $mech->get_ok($token_url); - my ($token_url) = $email->body =~ m{(http://\S+)}; - ok $token_url, "Found a token url $token_url"; + # check that we are on '/report/new' + is $mech->uri->path, '/report/new', "sent to /report/new"; - # go to the token url - $mech->get_ok($token_url); + # check that fields are prefilled for us + is_deeply $mech->visible_form_values, + { + name => 'Test User ll', + detail => 'This is a test report ll', + photo1 => '', + photo2 => '', + photo3 => '', + phone => '', + email => 'test-ll@example.com', + }, + "check imported fields are shown" + or diag Dumper( $mech->visible_form_values ); use Data::Dumper; - # check that we are on '/report/new' - is $mech->uri->path, '/report/new', "sent to /report/new"; - - # check that fields are prefilled for us - is_deeply $mech->visible_form_values, - { - name => 'Test User ll', - title => 'Test report ll', - detail => 'This is a test report ll', - photo => '', - phone => '', - may_show_name => '1', - }, - "check imported fields are shown"; - - my $user = - FixMyStreet::App->model('DB::User') - ->find( { email => 'test-ll@example.com' } ); - ok $user, "Found a user"; - - my $report = $user->problems->first; - is $report->state, 'partial', 'is still partial'; - is $report->title, 'Test report ll', 'title is correct'; - is $report->lang, 'nb', 'language is correct'; - - $mech->delete_user($user); - } + my $user = + FixMyStreet::App->model('DB::User') + ->find( { email => 'test-ll@example.com' } ); + ok $user, "Found a user"; + + my $report = $user->problems->first; + is $report->state, 'partial', 'is still partial'; + is $report->title, 'Test report ll', 'title is correct'; + is $report->lang, 'de-ch', 'language is correct'; + + $mech->delete_user($user); + }; }; done_testing(); + +END { + $mech->delete_body($body); +} diff --git a/t/app/controller/report_inspect.t b/t/app/controller/report_inspect.t new file mode 100644 index 000000000..69e43ad99 --- /dev/null +++ b/t/app/controller/report_inspect.t @@ -0,0 +1,248 @@ +use strict; +use warnings; +use Test::More; + +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $brum = $mech->create_body_ok(2514, 'Birmingham City Council', id => 2514); +my $oxon = $mech->create_body_ok(2237, 'Oxfordshire County Council', id => 2237); +my $contact = $mech->create_contact_ok( body_id => $oxon->id, category => 'Cows', email => 'cows@example.net' ); +my $rp = FixMyStreet::DB->resultset("ResponsePriority")->create({ + body => $oxon, + name => 'High Priority', +}); +FixMyStreet::DB->resultset("ContactResponsePriority")->create({ + contact => $contact, + response_priority => $rp, +}); +my $wodc = $mech->create_body_ok(2420, 'West Oxfordshire District Council', id => 2420); +$mech->create_contact_ok( body_id => $wodc->id, category => 'Horses', email => 'horses@example.net' ); + + +my ($report, $report2) = $mech->create_problems_for_body(2, $oxon->id, 'Test', { + category => 'Cows', cobrand => 'fixmystreet', areas => ',2237,2420', + whensent => \'current_timestamp', + latitude => 51.847693, longitude => -1.355908, +}); +my $report_id = $report->id; +my $report2_id = $report2->id; + + +my $user = $mech->log_in_ok('test@example.com'); +$user->update( { from_body => $oxon } ); + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => 'fixmystreet', +}, sub { + subtest "test inspect page" => sub { + $mech->get_ok("/report/$report_id"); + $mech->content_lacks('Save changes'); + $mech->content_lacks('Priority'); + $mech->content_lacks('Traffic management'); + + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_edit_priority' }); + $mech->get_ok("/report/$report_id"); + $mech->content_contains('Save changes'); + $mech->content_contains('Priority'); + $mech->content_lacks('Traffic management'); + + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_inspect' }); + $mech->get_ok("/report/$report_id"); + $mech->content_contains('Save changes'); + $mech->content_contains('Priority'); + $mech->content_contains('Traffic management'); + }; + + subtest "test basic inspect submission" => sub { + $mech->submit_form_ok({ button => 'save', with_fields => { traffic_information => 'Yes', state => 'Action Scheduled', include_update => undef } }); + $report->discard_changes; + is $report->state, 'action scheduled', 'report state changed'; + is $report->get_extra_metadata('traffic_information'), 'Yes', 'report data changed'; + }; + + subtest "test inspect & instruct submission" => sub { + $report->unset_extra_metadata('inspected'); + $report->state('confirmed'); + $report->update; + $report->inspection_log_entry->delete; + my $reputation = $report->user->get_extra_metadata("reputation"); + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ button => 'save', with_fields => { public_update => "This is a public update.", include_update => "1", state => 'action scheduled' } }); + $report->discard_changes; + is $report->comments->first->text, "This is a public update.", 'Update was created'; + is $report->get_extra_metadata('inspected'), 1, 'report marked as inspected'; + is $report->user->get_extra_metadata('reputation'), $reputation, "User reputation wasn't changed"; + }; + + subtest "test update is required when instructing" => sub { + $report->unset_extra_metadata('inspected'); + $report->update; + $report->inspection_log_entry->delete; + $report->comments->delete_all; + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ button => 'save', with_fields => { public_update => undef, include_update => "1" } }); + is_deeply $mech->page_errors, [ "Please provide a public update for this report." ], 'errors match'; + $report->discard_changes; + is $report->comments->count, 0, "Update wasn't created"; + is $report->get_extra_metadata('inspected'), undef, 'report not marked as inspected'; + }; + + subtest "test location changes" => sub { + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ button => 'save', with_fields => { latitude => 55, longitude => -2 } }); + $mech->content_contains('Invalid location'); + $mech->submit_form_ok({ button => 'save', with_fields => { latitude => 51.754926, longitude => -1.256179 } }); + $mech->content_lacks('Invalid location'); + }; + + subtest "test duplicate reports are shown" => sub { + my $old_state = $report->state; + $report->set_extra_metadata('duplicate_of' => $report2->id); + $report->state('duplicate'); + $report->update; + + $mech->get_ok("/report/$report_id"); + $mech->content_contains($report2->title); + + $mech->get_ok("/report/$report2_id"); + $mech->content_contains($report->title); + + $report->unset_extra_metadata('duplicate_of'); + $report->state($old_state); + $report->update; + }; + + subtest "marking a report as a duplicate with update correctly sets update status" => sub { + my $old_state = $report->state; + $report->comments->delete_all; + + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ button => 'save', with_fields => { state => 'Duplicate', duplicate_of => $report2->id, public_update => "This is a duplicate.", include_update => "1" } }); + $report->discard_changes; + + is $report->state, 'duplicate', 'report marked as duplicate'; + is $report->comments->search({ problem_state => 'duplicate' })->count, 1, 'update marking report as duplicate was left'; + + $report->update({ state => $old_state }); + }; + + subtest "marking a report as a duplicate doesn't clobber user-provided update" => sub { + my $old_state = $report->state; + $report->comments->delete_all; + + $mech->get_ok("/report/$report_id"); + my $update_text = "This text was entered as an update by the user."; + $mech->submit_form_ok({ button => 'save', with_fields => { + state => 'Duplicate', + duplicate_of => $report2->id, + public_update => $update_text, + include_update => "1", + }}); + $report->discard_changes; + + is $report->state, 'duplicate', 'report marked as duplicate'; + is $report->comments->search({ problem_state => 'duplicate' })->count, 1, 'update marked report as duplicate'; + $mech->content_contains($update_text); + $mech->content_lacks("Thank you for your report. This problem has already been reported."); + + $report->update({ state => $old_state }); + }; + + foreach my $test ( + { type => 'report_edit_priority', priority => 1 }, + { type => 'report_edit_category', category => 1 }, + { type => 'report_inspect', priority => 1, category => 1, detailed => 1 }, + ) { + subtest "test $test->{type} permission" => sub { + $user->user_body_permissions->delete; + $user->user_body_permissions->create({ body => $oxon, permission_type => $test->{type} }); + $mech->get_ok("/report/$report_id"); + $mech->contains_or_lacks($test->{priority}, 'Priority</label>'); + $mech->contains_or_lacks($test->{priority}, '>High'); + $mech->contains_or_lacks($test->{category}, 'Category'); + $mech->contains_or_lacks($test->{detailed}, 'Extra details'); + $mech->submit_form_ok({ + button => 'save', + with_fields => { + $test->{priority} ? (priority => 1) : (), + $test->{category} ? (category => 'Cows') : (), + $test->{detailed} ? (detailed_information => 'Highland ones') : (), + } + }); + }; + } +}; + +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'oxfordshire', +}, sub { + subtest "test negative reputation" => sub { + my $reputation = $report->user->get_extra_metadata("reputation") || 0; + + $mech->get_ok("/report/$report_id"); + $mech->submit_form( button => 'remove_from_site' ); + + $report->discard_changes; + is $report->user->get_extra_metadata('reputation'), $reputation-1, "User reputation was decreased"; + $report->update({ state => 'confirmed' }); + }; + + subtest "test positive reputation" => sub { + $report->unset_extra_metadata('inspected'); + $report->update; + $report->inspection_log_entry->delete if $report->inspection_log_entry; + my $reputation = $report->user->get_extra_metadata("reputation") || 0; + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ button => 'save', with_fields => { state => 'action scheduled', include_update => undef } }); + $report->discard_changes; + is $report->get_extra_metadata('inspected'), 1, 'report marked as inspected'; + is $report->user->get_extra_metadata('reputation'), $reputation+1, "User reputation was increased"; + }; + + subtest "Oxfordshire-specific traffic management options are shown" => sub { + $report->update({ state => 'confirmed' }); + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ button => 'save', with_fields => { traffic_information => 'Signs and Cones', state => 'Action Scheduled', include_update => undef } }); + $report->discard_changes; + is $report->state, 'action scheduled', 'report state changed'; + is $report->get_extra_metadata('traffic_information'), 'Signs and Cones', 'report data changed'; + }; + +}; + +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire', 'fixmystreet' ], + BASE_URL => 'http://fixmystreet.site', +}, sub { + subtest "test category/body changes" => sub { + $mech->host('oxfordshire.fixmystreet.site'); + $report->update({ state => 'confirmed' }); + $mech->get_ok("/report/$report_id"); + # Then change the category to the other council in this location, + # which should cause it to be resent. We clear the host because + # otherwise testing stays on host() above. + $mech->clear_host; + $mech->submit_form(button => 'save', with_fields => { category => 'Horses', include_update => undef, }); + + $report->discard_changes; + is $report->category, "Horses", "Report in correct category"; + is $report->whensent, undef, "Report marked as unsent"; + is $report->bodies_str, $wodc->id, "Reported to WODC"; + + is $mech->uri->path, "/report/$report_id", "redirected to correct page"; + is $mech->res->code, 200, "got 200 for final destination"; + is $mech->res->previous->code, 302, "got 302 for redirect"; + # Extra check given host weirdness + is $mech->res->previous->header('Location'), "http://fixmystreet.site/report/$report_id"; + }; +}; + + +END { + $mech->delete_body($oxon); + $mech->delete_body($brum); + done_testing(); +} diff --git a/t/app/controller/report_interest_count.t b/t/app/controller/report_interest_count.t index 0a8a5ac0b..3cb80ea5f 100644 --- a/t/app/controller/report_interest_count.t +++ b/t/app/controller/report_interest_count.t @@ -1,5 +1,16 @@ use strict; use warnings; + +package FixMyStreet::Cobrand::Tester; + +use parent 'FixMyStreet::Cobrand::Default'; + +sub can_support_problems { + return 1; +} + +package main; + use Test::More; use FixMyStreet::TestMech; @@ -11,15 +22,7 @@ my $mech = FixMyStreet::TestMech->new; # create a test user and report $mech->delete_user('test@example.com'); -my $user = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'test@example.com', name => 'Test User' } ); -ok $user, "created test user"; - -my $user2 = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'test2@example.com', name => 'Other User' } ); -ok $user2, "created test user"; +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); my $dt = DateTime->new( year => 2011, @@ -33,7 +36,7 @@ my $dt = DateTime->new( my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { postcode => 'SW1A 1AA', - council => '2504', + bodies_str => '2504', areas => ',105255,11806,11828,2247,2504,', category => 'Other', title => 'Test 2', @@ -56,37 +59,38 @@ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( my $report_id = $report->id; ok $report, "created test report - $report_id"; -SKIP: { - skip( "Need 'fixmybarangay' in ALLOWED_COBRANDS config", 29 ) - unless FixMyStreet::Cobrand->exists('fixmybarangay'); +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'tester' ], +}, sub { + my $body = $mech->create_body_ok(2504, 'Westminster City Council'); + for my $test ( { - desc => 'if not from council then no supporter button', - from_body => 0, + desc => 'if not from body then no supporter button', + from_body => undef, support_string => 'No supporters', }, { - desc => 'from council user can increment supported count', - from_body => 2504, + desc => 'from body user can increment supported count', + from_body => $body->id, support_string => 'No supporters', updated_support => '1 supporter' }, { desc => 'correct grammar for more than one supporter', - from_body => 2504, + from_body => $body->id, support_string => '1 supporter', updated_support => '2 supporters' }, ) { subtest $test->{desc} => sub { - ok $mech->host('fixmybarangay.com'), 'changed to fixmybarangay'; $mech->log_in_ok( $user->email ); $user->from_body( $test->{from_body} ); $user->update; - $report->discard_changes; - $report->council( $test->{report_council} ); - $report->update; + $report->update( { + bodies_str => $test->{report_council} + } ); $mech->get_ok("/report/$report_id"); $mech->content_contains( $test->{support_string} ); @@ -95,7 +99,7 @@ SKIP: { $mech->content_contains('Add support'); $mech->submit_form_ok( { form_number => 1 } ); - is $mech->uri, "http://fixmybarangay.com/report/$report_id", 'add support redirects to report page'; + is $mech->uri->path, "/report/$report_id", 'add support redirects to report page'; $mech->content_contains($test->{updated_support}); } else { @@ -104,29 +108,23 @@ SKIP: { }; } - subtest 'check non council user cannot increment support count' => sub { - ok $mech->host('fixmybarangay.com'), 'changed to fixmybarangay'; - $report->discard_changes; - $report->interest_count(1); - ok $report->update(), 'updated interest count'; - - $report->discard_changes; + subtest 'check non body user cannot increment support count' => sub { + ok $report->update({ interest_count => 1 }), 'updated interest count'; is $report->interest_count, 1, 'correct interest count'; $mech->get_ok("/report/$report_id"); $mech->content_contains( '1 supporter' ); + $mech->log_out_ok( $user->email ); $mech->post_ok("/report/support", { id => $report_id } ); - is $mech->uri, "http://fixmybarangay.com/report/$report_id", 'add support redirects to report page'; + is $mech->uri->path, "/report/$report_id", 'add support redirects to report page'; $mech->content_contains( '1 supporter' ); }; }; subtest 'check support details not shown if not enabled in cobrand' => sub { - ok $mech->host('fixmystreet.com'), 'changed to fixmystreet'; - $report->interest_count(1); ok $report->update, 'updated interest count'; @@ -134,10 +132,7 @@ subtest 'check support details not shown if not enabled in cobrand' => sub { $mech->content_lacks( '1 supporter' ); }; -$report->discard_changes; -$report->council( 2504 ); -$report->update; - -# tidy up -$mech->delete_user('test@example.com'); -done_testing(); +END { + $mech->delete_user('test@example.com'); + done_testing(); +} diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t index 92943bde9..71090cd26 100644 --- a/t/app/controller/report_new.t +++ b/t/app/controller/report_new.t @@ -2,12 +2,16 @@ use strict; use utf8; # sign in error message has – in it use warnings; use Test::More; -use utf8; use FixMyStreet::TestMech; +use FixMyStreet::App; use Web::Scraper; use Path::Class; +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + my $mech = FixMyStreet::TestMech->new; $mech->get_ok('/report/new'); @@ -17,86 +21,146 @@ ok -e $sample_file, "sample file $sample_file exists"; subtest "test that bare requests to /report/new get redirected" => sub { $mech->get_ok('/report/new'); - is $mech->uri->path, '/around', "went to /around"; + is $mech->uri->path, '/', "went to /"; is_deeply { $mech->uri->query_form }, {}, "query empty"; - $mech->get_ok('/report/new?pc=SW1A%201AA'); + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/report/new?pc=SW1A%201AA'); + }; is $mech->uri->path, '/around', "went to /around"; is_deeply { $mech->uri->query_form }, { pc => 'SW1A 1AA' }, "pc correctly transferred"; }; -my %contact_params = ( - confirmed => 1, - deleted => 0, - editor => 'Test', - whenedited => \'current_timestamp', - note => 'Created for test', -); +my %body_ids; +my @bodies; +for my $body ( + { area_id => 2651, name => 'City of Edinburgh Council' }, + { area_id => 2226, name => 'Gloucestershire County Council' }, + { area_id => 2326, name => 'Cheltenham Borough Council' }, + { area_id => 2504, name => 'Westminster City Council' }, + # The next three have fixed IDs because bits of the code rely on + # the body ID === MapIt area ID. + { area_id => 2482, name => 'Bromley Council', id => 2482 }, + { area_id => 2227, name => 'Hampshire County Council', id => 2227 }, + { area_id => 2333, name => 'Hart Council', id => 2333 }, +) { + my $body_obj = $mech->create_body_ok($body->{area_id}, $body->{name}, id => $body->{id}); + push @bodies, $body_obj; + $body_ids{$body->{area_id}} = $body_obj->id; +} + # Let's make some contacts to send things to! -FixMyStreet::App->model('DB::Contact')->search( { - email => { 'like', '%example.com' }, -} )->delete; -my $contact1 = FixMyStreet::App->model('DB::Contact')->find_or_create( { - %contact_params, - body_id => 2651, # Edinburgh +my $contact1 = $mech->create_contact_ok( + body_id => $body_ids{2651}, # Edinburgh category => 'Street lighting', email => 'highways@example.com', -} ); -my $contact2 = FixMyStreet::App->model('DB::Contact')->find_or_create( { - %contact_params, - body_id => 2226, # Gloucestershire +); +my $contact2 = $mech->create_contact_ok( + body_id => $body_ids{2226}, # Gloucestershire category => 'Potholes', email => 'potholes@example.com', -} ); -my $contact3 = FixMyStreet::App->model('DB::Contact')->find_or_create( { - %contact_params, - body_id => 2326, # Cheltenham +); +my $contact3 = $mech->create_contact_ok( + body_id => $body_ids{2326}, # Cheltenham category => 'Trees', email => 'trees@example.com', -} ); -my $contact4 = FixMyStreet::App->model('DB::Contact')->find_or_create( { - %contact_params, - body_id => 2482, # Bromley +); +my $contact4 = $mech->create_contact_ok( + body_id => $body_ids{2482}, # Bromley category => 'Trees', email => 'trees@example.com', -} ); -my $contact5 = FixMyStreet::App->model('DB::Contact')->find_or_create( { - %contact_params, - body_id => 2651, # Edinburgh +); +my $contact5 = $mech->create_contact_ok( + body_id => $body_ids{2651}, # Edinburgh category => 'Trees', email => 'trees@example.com', -} ); -my $contact6 = FixMyStreet::App->model('DB::Contact')->find_or_create( { - %contact_params, - body_id => 2434, # Lichfield +); +my $contact6 = $mech->create_contact_ok( + body_id => $body_ids{2333}, # Hart category => 'Trees', email => 'trees@example.com', -} ); -my $contact7 = FixMyStreet::App->model('DB::Contact')->find_or_create( { - %contact_params, - body_id => 2240, # Lichfield - category => 'Street lighting', +); +my $contact7 = $mech->create_contact_ok( + body_id => $body_ids{2227}, # Hampshire + category => 'Street lighting', email => 'highways@example.com', -} ); -ok $contact1, "created test contact 1"; -ok $contact2, "created test contact 2"; -ok $contact3, "created test contact 3"; -ok $contact4, "created test contact 4"; -ok $contact5, "created test contact 5"; -ok $contact6, "created test contact 6"; -ok $contact7, "created test contact 7"; +); +my $contact8 = $mech->create_contact_ok( + body_id => $body_ids{2504}, + category => 'Street lighting', + email => 'highways@example.com' +); # test that the various bit of form get filled in and errors correctly # generated. foreach my $test ( { msg => 'all fields empty', + pc => 'OX1 3DH', + fields => { + title => '', + detail => '', + photo1 => '', + photo2 => '', + photo3 => '', + name => '', + may_show_name => '1', + email => '', + phone => '', + password_sign_in => '', + password_register => '', + remember_me => undef, + }, + changes => {}, + errors => [ + 'Please enter a subject', + 'Please enter some details', + # No category error, as no categories for Oxon at all, so is skipped + 'Please enter your email', + 'Please enter your name', + ], + }, + { + msg => 'all fields empty, bad category', + pc => 'GL50 2PR', + fields => { + title => '', + detail => '', + photo1 => '', + photo2 => '', + photo3 => '', + name => '', + may_show_name => '1', + email => '', + phone => '', + category => 'Something bad', + password_sign_in => '', + password_register => '', + remember_me => undef, + }, + changes => { + category => '-- Pick a category --', + }, + errors => [ + 'Please enter a subject', + 'Please enter some details', + 'Please choose a category', + 'Please enter your email', + 'Please enter your name', + ], + }, + { + msg => 'all fields empty except category', pc => 'SW1A 1AA', fields => { title => '', detail => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => '', may_show_name => '1', email => '', @@ -120,7 +184,9 @@ foreach my $test ( fields => { title => '', detail => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => '', may_show_name => undef, email => '', @@ -144,7 +210,9 @@ foreach my $test ( fields => { title => '', detail => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => 'Bob Jones', may_show_name => undef, email => '', @@ -167,7 +235,9 @@ foreach my $test ( fields => { title => '', detail => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => 'Bob Jones', may_show_name => '1', email => '', @@ -190,7 +260,9 @@ foreach my $test ( fields => { title => "DOG SHIT\r\nON WALLS", detail => "on this portakabin -\r\n\r\nmore of a portaloo HEH!!", - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => 'Bob Jones', may_show_name => '1', email => '', @@ -213,7 +285,9 @@ foreach my $test ( fields => { title => 'Test title', detail => 'Test detail', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => 'DUDE', may_show_name => '1', email => '', @@ -235,7 +309,9 @@ foreach my $test ( fields => { title => 'Test title', detail => 'Test detail', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => 'anonymous', may_show_name => '1', email => '', @@ -257,7 +333,9 @@ foreach my $test ( fields => { title => 'Test title', detail => 'Test detail', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => 'Joe Smith', may_show_name => '1', email => 'not an email', @@ -276,7 +354,9 @@ foreach my $test ( fields => { title => " Test title ", detail => " first line \n\n second\nline\n\n ", - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => '', may_show_name => '1', email => '', @@ -301,7 +381,9 @@ foreach my $test ( fields => { title => '', detail => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => ' Bob Jones ', may_show_name => '1', email => ' BOB @ExAmplE.COM ', @@ -323,7 +405,9 @@ foreach my $test ( fields => { title => 'Title', detail => 'Detail', - photo => [ [ undef, 'bad.txt', Content => 'This is not a JPEG', Content_Type => 'text/plain' ], 1 ], + photo1 => [ [ undef, 'bad.txt', Content => 'This is not a JPEG', Content_Type => 'text/plain' ], 1 ], + photo2 => '', + photo3 => '', name => 'Bob Jones', may_show_name => '1', email => 'bob@example.com', @@ -334,9 +418,9 @@ foreach my $test ( remember_me => undef, }, changes => { - photo => '', + photo1 => '', }, - errors => [ "Please upload a JPEG image only" ], + errors => [ "Please upload an image only" ], }, { msg => 'bad photo upload gives error', @@ -344,7 +428,9 @@ foreach my $test ( fields => { title => 'Title', detail => 'Detail', - photo => [ [ undef, 'fake.jpeg', Content => 'This is not a JPEG', Content_Type => 'image/jpeg' ], 1 ], + photo1 => [ [ undef, 'fake.jpeg', Content => 'This is not a JPEG', Content_Type => 'image/jpeg' ], 1 ], + photo2 => '', + photo3 => '', name => 'Bob Jones', may_show_name => '1', email => 'bob@example.com', @@ -355,9 +441,9 @@ foreach my $test ( remember_me => undef, }, changes => { - photo => '', + photo1 => '', }, - errors => [ "That image doesn't appear to have uploaded correctly (Please upload a JPEG image only ), please try again." ], + errors => [ "That image doesn't appear to have uploaded correctly (Please upload an image only ), please try again." ], }, { msg => 'photo with octet-stream gets through okay', @@ -365,7 +451,9 @@ foreach my $test ( fields => { title => '', detail => 'Detail', - photo => [ [ $sample_file, undef, Content_Type => 'application/octet-stream' ], 1 ], + photo1 => [ [ $sample_file, undef, Content_Type => 'application/octet-stream' ], 1 ], + photo2 => '', + photo3 => '', name => 'Bob Jones', may_show_name => '1', email => 'bob@example.com', @@ -376,7 +464,7 @@ foreach my $test ( remember_me => undef, }, changes => { - photo => '', + photo1 => '', }, errors => [ "Please enter a subject" ], }, @@ -386,20 +474,25 @@ foreach my $test ( $mech->get_ok('/around'); # submit initial pc form - $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, - "submit location" ); - is_deeply $mech->page_errors, [], "no errors for pc '$test->{pc}'"; - - # click through to the report page - $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, - "follow 'skip this step' link" ); - - # submit the main form - $mech->submit_form_ok( { with_fields => $test->{fields} }, - "submit form" ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, + "submit location" ); + is_deeply $mech->page_errors, [], "no errors for pc '$test->{pc}'"; + + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); + + # submit the main form + $mech->submit_form_ok( { with_fields => $test->{fields} }, + "submit form" ); + }; # check that we got the errors expected - is_deeply $mech->page_errors, $test->{errors}, "check errors"; + is_deeply [ sort @{$mech->page_errors} ], [ sort @{$test->{errors}} ], "check errors"; # check that fields have changed as expected my $new_values = { @@ -454,30 +547,35 @@ foreach my $test ( # submit initial pc form $mech->get_ok('/around'); - $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, - "submit location" ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, + "submit location" ); - # click through to the report page - $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, - "follow 'skip this step' link" ); + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); - $mech->submit_form_ok( - { - button => 'submit_register', - with_fields => { - title => 'Test Report', - detail => 'Test report details.', - photo => '', - name => 'Joe Bloggs', - may_show_name => '1', - email => 'test-1@example.com', - phone => '07903 123 456', - category => 'Street lighting', - password_register => $test->{password} ? 'secret' : '', - } - }, - "submit good details" - ); + $mech->submit_form_ok( + { + button => 'submit_register', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + photo1 => '', + name => 'Joe Bloggs', + may_show_name => '1', + email => 'test-1@example.com', + phone => '07903 123 456', + category => 'Street lighting', + password_register => $test->{password} ? 'secret' : '', + } + }, + "submit good details" + ); + }; # check that we got the errors expected is_deeply $mech->page_errors, [], "check there were no errors"; @@ -503,15 +601,14 @@ foreach my $test ( is $mech->get( '/report/' . $report->id )->code, 404, "report not found"; # Check the report has been assigned appropriately - is $report->council, 2651; + is $report->bodies_str, $body_ids{2651}; # receive token my $email = $mech->get_email; ok $email, "got an email"; - like $email->body, qr/confirm the problem/i, "confirm the problem"; + like $mech->get_text_body_from_email($email), qr/confirm that you want to send your\s+report/i, "confirm the problem"; - my ($url) = $email->body =~ m{(http://\S+)}; - ok $url, "extracted confirm url '$url'"; + my $url = $mech->get_link_from_email($email); # confirm token $mech->get_ok($url); @@ -555,8 +652,7 @@ subtest "test password errors for a user who is signing in as they report" => su # check that the user does not exist my $test_email = 'test-2@example.com'; - my $user = FixMyStreet::App->model('DB::User')->find_or_create( { email => $test_email } ); - ok $user, "test user does exist"; + my $user = $mech->create_user_ok($test_email); # setup the user. ok $user->update( { @@ -567,27 +663,32 @@ subtest "test password errors for a user who is signing in as they report" => su # submit initial pc form $mech->get_ok('/around'); - $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, - "submit location" ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, + "submit location" ); - # click through to the report page - $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, - "follow 'skip this step' link" ); + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); - $mech->submit_form_ok( - { - button => 'submit_sign_in', - with_fields => { - title => 'Test Report', - detail => 'Test report details.', - photo => '', - email => 'test-2@example.com', - password_sign_in => 'secret1', - category => 'Street lighting', - } - }, - "submit with wrong password" - ); + $mech->submit_form_ok( + { + button => 'submit_sign_in', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + photo1 => '', + email => 'test-2@example.com', + password_sign_in => 'secret1', + category => 'Street lighting', + } + }, + "submit with wrong password" + ); + }; # check that we got the errors expected is_deeply $mech->page_errors, [ @@ -597,13 +698,13 @@ subtest "test password errors for a user who is signing in as they report" => su subtest "test report creation for a user who is signing in as they report" => sub { $mech->log_out_ok; + $mech->cookie_jar({}); $mech->clear_emails_ok; # check that the user does not exist my $test_email = 'test-2@example.com'; - my $user = FixMyStreet::App->model('DB::User')->find_or_create( { email => $test_email } ); - ok $user, "test user does exist"; + my $user = $mech->create_user_ok($test_email); # setup the user. ok $user->update( { @@ -614,52 +715,54 @@ subtest "test report creation for a user who is signing in as they report" => su # submit initial pc form $mech->get_ok('/around'); - $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, - "submit location" ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, + "submit location" ); - # click through to the report page - $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, - "follow 'skip this step' link" ); + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); - $mech->submit_form_ok( - { - button => 'submit_sign_in', - with_fields => { - title => 'Test Report', - detail => 'Test report details.', - photo => '', - email => 'test-2@example.com', - password_sign_in => 'secret2', - category => 'Street lighting', - } - }, - "submit good details" - ); + $mech->submit_form_ok( + { + button => 'submit_sign_in', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + photo1 => '', + email => 'test-2@example.com', + password_sign_in => 'secret2', + category => 'Street lighting', + } + }, + "submit good details" + ); - # check that we got the errors expected - is_deeply $mech->page_errors, [ - 'You have successfully signed in; please check and confirm your details are accurate:', - ], "check there were errors"; + # check that we got the message expected + $mech->content_contains( 'You have successfully signed in; please check and confirm your details are accurate:' ); - # Now submit with a name - $mech->submit_form_ok( - { - with_fields => { - name => 'Joe Bloggs', - } - }, - "submit good details" - ); + # Now submit with a name + $mech->submit_form_ok( + { + with_fields => { + name => 'Joe Bloggs', + } + }, + "submit good details" + ); + }; # find the report my $report = $user->problems->first; ok $report, "Found the report"; - # check that we got redirected to /report/ - is $mech->uri->path, "/report/" . $report->id, "redirected to report page"; + $mech->content_contains('Thank you for reporting this issue'); # Check the report has been assigned appropriately - is $report->council, 2651; + is $report->bodies_str, $body_ids{2651}; # check that no emails have been sent $mech->email_count_is(0); @@ -708,52 +811,58 @@ foreach my $test ( # submit initial pc form $mech->get_ok('/around'); - $mech->submit_form_ok( { with_fields => { pc => 'GL50 2PR', } }, - "submit location" ); - - # click through to the report page - $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, - "follow 'skip this step' link" ); - - # check that the fields are correctly prefilled - is_deeply( - $mech->visible_form_values, - { - title => '', - detail => '', - may_show_name => '1', - name => 'Test User', - phone => '01234 567 890', - photo => '', - category => '-- Pick a category --', - }, - "user's details prefilled" - ); - - $mech->submit_form_ok( - { - with_fields => { - title => "Test Report at café", - detail => 'Test report details.', - photo => '', - name => 'Joe Bloggs', + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'GL50 2PR', } }, + "submit location" ); + + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); + + # check that the fields are correctly prefilled + is_deeply( + $mech->visible_form_values, + { + title => '', + detail => '', may_show_name => '1', - phone => '07903 123 456', - category => $test->{category}, - } - }, - "submit good details" - ); + name => 'Test User', + phone => '01234 567 890', + photo1 => '', + photo2 => '', + photo3 => '', + category => '-- Pick a category --', + }, + "user's details prefilled" + ); + + $mech->submit_form_ok( + { + with_fields => { + title => "Test Report at café", + detail => 'Test report details.', + photo1 => '', + name => 'Joe Bloggs', + may_show_name => '1', + phone => '07903 123 456', + category => $test->{category}, + } + }, + "submit good details" + ); + }; # find the report my $report = $user->problems->first; ok $report, "Found the report"; # Check the report has been assigned appropriately - is $report->council, $test->{council}; + is $report->bodies_str, $body_ids{$test->{council}}; - # check that we got redirected to /report/ - is $mech->uri->path, "/report/" . $report->id, "redirected to report page"; + $mech->content_contains('Thank you for reporting this issue'); # check that no emails have been sent $mech->email_count_is(0); @@ -795,34 +904,38 @@ subtest "test report creation for a category that is non public" => sub { # check that the user does not exist my $test_email = 'test-2@example.com'; - my $user = FixMyStreet::App->model('DB::User')->find_or_create( { email => $test_email } ); - ok $user, "test user does exist"; + my $user = $mech->create_user_ok($test_email); $contact1->update( { non_public => 1 } ); # submit initial pc form $mech->get_ok('/around'); - $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, - "submit location" ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, + "submit location" ); - # click through to the report page - $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, - "follow 'skip this step' link" ); + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); - $mech->submit_form_ok( - { - button => 'submit_register', - with_fields => { - title => 'Test Report', - detail => 'Test report details.', - photo => '', - email => 'test-2@example.com', - name => 'Joe Bloggs', - category => 'Street lighting', - } - }, - "submit good details" - ); + $mech->submit_form_ok( + { + button => 'submit_register', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + photo1 => '', + email => 'test-2@example.com', + name => 'Joe Bloggs', + category => 'Street lighting', + } + }, + "submit good details" + ); + }; # find the report my $report = $user->problems->first; @@ -833,10 +946,9 @@ subtest "test report creation for a category that is non public" => sub { my $email = $mech->get_email; ok $email, "got an email"; - like $email->body, qr/confirm the problem/i, "confirm the problem"; + like $mech->get_text_body_from_email($email), qr/confirm that you want to send your\s+report/i, "confirm the problem"; - my ($url) = $email->body =~ m{(http://\S+)}; - ok $url, "extracted confirm url '$url'"; + my $url = $mech->get_link_from_email($email); # confirm token $mech->get_ok($url); @@ -858,8 +970,24 @@ subtest "test report creation for a category that is non public" => sub { $contact2->category( "Pothol\xc3\xa9s" ); $contact2->update; -$mech->get_ok( '/report/new/ajax?latitude=' . $saved_lat . '&longitude=' . $saved_lon ); + +my $extra_details; +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', +}, sub { + $extra_details = $mech->get_ok_json( '/report/new/ajax?latitude=' . $saved_lat . '&longitude=' . $saved_lon ); +}; $mech->content_contains( "Pothol\xc3\xa9s" ); +ok !$extra_details->{titles_list}, 'Non Bromley does not send back list of titles'; + +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', +}, sub { + $extra_details = $mech->get_ok_json( '/report/new/ajax?latitude=51.4021&longitude=0.01578'); +}; +ok $extra_details->{titles_list}, 'Bromley sends back list of titles'; #### test uploading an image @@ -867,18 +995,22 @@ $mech->content_contains( "Pothol\xc3\xa9s" ); #### possibly manual testing # create report without using map -# create report by clicking on may with javascript off +# create report by clicking on map with javascript off # create report with images off subtest "check that a lat/lon off coast leads to /around" => sub { my $off_coast_latitude = 50.78301; my $off_coast_longitude = -0.646929; - $mech->get_ok( # - "/report/new" - . "?latitude=$off_coast_latitude" - . "&longitude=$off_coast_longitude" - ); + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok( # + "/report/new" + . "?latitude=$off_coast_latitude" + . "&longitude=$off_coast_longitude" + ); + }; is $mech->uri->path, '/around', "redirected to '/around'"; @@ -892,15 +1024,15 @@ subtest "check that a lat/lon off coast leads to /around" => sub { for my $test ( { desc => 'user title not set if not bromley problem', - host => 'http://www.fixmystreet.com', - postcode => 'EH99 1SP', + host => 'www.fixmystreet.com', + postcode => 'EH1 1BB', fms_extra_title => '', - extra => undef, + extra => [], user_title => undef, }, { desc => 'title shown for bromley problem on main site', - host => 'http://www.fixmystreet.com', + host => 'www.fixmystreet.com', postcode => 'BR1 3UH', fms_extra_title => 'MR', extra => [ @@ -915,7 +1047,7 @@ for my $test ( { desc => 'title, first and last name shown for bromley problem on cobrand', - host => 'http://bromley.fixmystreet.com', + host => 'bromley.fixmystreet.com', postcode => 'BR1 3UH', first_name => 'Test', last_name => 'User', @@ -942,9 +1074,10 @@ for my $test ( ) { subtest $test->{desc} => sub { - if ( $test->{host} =~ /bromley/ && !FixMyStreet::Cobrand->exists('bromley') ) { - plan skip_all => 'Skipping Bromley tests without Bromley cobrand'; - } + my $override = { + ALLOWED_COBRANDS => [ $test->{host} =~ /bromley/ ? 'bromley' : 'fixmystreet' ], + MAPIT_URL => 'http://mapit.uk/', + }; $mech->host( $test->{host} ); @@ -952,12 +1085,14 @@ for my $test ( $mech->clear_emails_ok; $mech->get_ok('/'); - $mech->submit_form_ok( { with_fields => { pc => $test->{postcode}, } }, - "submit location" ); - $mech->follow_link_ok( - { text_regex => qr/skip this step/i, }, - "follow 'skip this step' link" - ); + FixMyStreet::override_config $override, sub { + $mech->submit_form_ok( { with_fields => { pc => $test->{postcode}, } }, + "submit location" ); + $mech->follow_link_ok( + { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" + ); + }; my $fields = $mech->visible_form_values('mapSkippedForm'); if ( $test->{fms_extra_title} ) { @@ -980,7 +1115,7 @@ for my $test ( my $submission_fields = { title => "Test Report", detail => 'Test report details.', - photo => '', + photo1 => '', email => 'firstlast@example.com', may_show_name => '1', phone => '07903 123 456', @@ -999,15 +1134,16 @@ for my $test ( $submission_fields->{name} = 'Test User'; } - $mech->submit_form_ok( { with_fields => $submission_fields }, - "submit good details" ); + FixMyStreet::override_config $override, sub { + $mech->submit_form_ok( { with_fields => $submission_fields }, + "submit good details" ); + }; my $email = $mech->get_email; ok $email, "got an email"; - like $email->body, qr/confirm the problem/i, "confirm the problem"; + like $mech->get_text_body_from_email($email), qr/confirm that you want to send your\s+report/i, "confirm the problem"; - my ($url) = $email->body =~ m{(https?://\S+)}; - ok $url, "extracted confirm url '$url'"; + my $url = $mech->get_link_from_email($email); # confirm token in order to update the user details $mech->get_ok($url); @@ -1018,7 +1154,7 @@ for my $test ( my $report = $user->problems->first; ok $report, "Found the report"; - my $extras = $report->extra; + my $extras = $report->get_extra_fields; is $user->title, $test->{'user_title'}, 'user title correct'; is_deeply $extras, $test->{extra}, 'extra contains correct values'; @@ -1030,7 +1166,7 @@ for my $test ( subtest 'user title not reset if no user title in submission' => sub { $mech->log_out_ok; - $mech->host( 'http://fixmystreet.com' ); + $mech->host( 'www.fixmystreet.com' ); my $user = $mech->log_in_ok( 'userwithtitle@example.com' ); @@ -1047,7 +1183,7 @@ subtest 'user title not reset if no user title in submission' => sub { my $submission_fields = { title => "Test Report", detail => 'Test report details.', - photo => '', + photo1 => '', name => 'Has Title', may_show_name => '1', phone => '07903 123 456', @@ -1055,18 +1191,23 @@ subtest 'user title not reset if no user title in submission' => sub { }; $mech->get_ok('/'); - $mech->submit_form_ok( { with_fields => { pc => 'EH99 1SP', } }, - "submit location" ); - $mech->follow_link_ok( - { text_regex => qr/skip this step/i, }, - "follow 'skip this step' link" - ); - - my $fields = $mech->visible_form_values('mapSkippedForm'); - ok !exists( $fields->{fms_extra_title} ), 'user title field not displayed'; + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, + "submit location" ); + $mech->follow_link_ok( + { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" + ); + + my $fields = $mech->visible_form_values('mapSkippedForm'); + ok !exists( $fields->{fms_extra_title} ), 'user title field not displayed'; - $mech->submit_form_ok( { with_fields => $submission_fields }, - "submit good details" ); + $mech->submit_form_ok( { with_fields => $submission_fields }, + "submit good details" ); + }; $user->discard_changes; my $report = $user->problems->first; @@ -1075,79 +1216,427 @@ subtest 'user title not reset if no user title in submission' => sub { is $user->title, 'MR', 'User title unchanged'; }; -SKIP: { - skip( "Need 'lichfielddc' in ALLOWED_COBRANDS config", 100 ) - unless FixMyStreet::Cobrand->exists('lichfielddc'); +subtest "test Hart" => sub { + for my $test ( + { + desc => 'confirm link for cobrand council in two tier cobrand links to cobrand site', + category => 'Trees', + council => 2333, + national => 0, + button => 'submit_register', + }, + { + desc => 'confirm link for non cobrand council in two tier cobrand links to national site', + category => 'Street Lighting', + council => 2227, + national => 1, + button => 'submit_register', + }, + { + desc => 'confirmation page for cobrand council in two tier cobrand links to cobrand site', + category => 'Trees', + council => 2333, + national => 0, + confirm => 1, + }, + { + desc => 'confirmation page for non cobrand council in two tier cobrand links to national site', + category => 'Street Lighting', + council => 2227, + national => 1, + confirm => 1, + }, + ) { + subtest $test->{ desc } => sub { + my $test_email = 'test-22@example.com'; + $mech->host( 'hart.fixmystreet.com' ); + $mech->clear_emails_ok; + $mech->log_out_ok; + + my $user = $mech->log_in_ok($test_email) if $test->{confirm}; + + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'hart', 'fixmystreet' ], + BASE_URL => 'http://www.fixmystreet.com', + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/around'); + $mech->content_contains( "Hart Council" ); + $mech->submit_form_ok( { with_fields => { pc => 'GU51 4AE' } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + my %optional_fields = $test->{confirm} ? () : + ( email => $test_email, phone => '07903 123 456' ); + + # we do this as otherwise test::www::mechanize::catalyst + # goes to the value set in ->host above irregardless and + # that is a 404. It works but it is not pleasant. + $mech->clear_host if $test->{confirm} && $test->{national}; + $mech->submit_form_ok( + { + button => $test->{button}, + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + photo1 => '', + name => 'Joe Bloggs', + may_show_name => '1', + category => $test->{category}, + %optional_fields + } + }, + "submit good details" + ); + }; + is_deeply $mech->page_errors, [], "check there were no errors"; + + # check that the user has been created/ not changed + $user = + FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + ok $user, "user found"; + + # find the report + my $report = $user->problems->first; + ok $report, "Found the report"; + + # Check the report has been assigned appropriately + is $report->bodies_str, $body_ids{$test->{council}}; + + if ( $test->{confirm} ) { + is $mech->uri->path, "/report/new"; + my $base = 'www.fixmystreet.com'; + $base = "hart.fixmystreet.com" unless $test->{national}; + $mech->content_contains("$base/report/" . $report->id, "links to correct site"); + } else { + # receive token + my $email = $mech->get_email; + ok $email, "got an email"; + my $body = $mech->get_text_body_from_email($email); + like $body, qr/to confirm that you want to send your/i, "confirm the problem"; + + # does it reference the fact that this report hasn't been sent to Hart? + if ( $test->{national} ) { + like $body, qr/Hart Council is not responsible for this type/i, "mentions report hasn't gone to Hart"; + } else { + unlike $body, qr/Hart Council is not responsible for this type/i, "doesn't mention report hasn't gone to Hart"; + } - my $test_email = 'test-22@example.com'; - $mech->host( 'http://lichfielddc.fixmystreet.com/' ); - $mech->clear_emails_ok; - $mech->log_out_ok; + my $url = $mech->get_link_from_email($email); + + # confirm token + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'hart', 'fixmystreet' ], + BASE_URL => 'http://www.fixmystreet.com', + }, sub { + $mech->get_ok($url); + }; + + my $base = 'www.fixmystreet.com'; + $base = 'hart.fixmystreet.com' unless $test->{national}; + $mech->content_contains( $base . '/report/' . + $report->id, 'confirm page links to correct site' ); + + if ( $test->{national} ) { + # Shouldn't be found, as it was a county problem + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'hart', 'fixmystreet' ], + }, sub { + is $mech->get( '/report/' . $report->id )->code, 404, "report not found"; + }; + + # But should be on the main site + $mech->host( 'www.fixmystreet.com' ); + } + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'hart', 'fixmystreet' ], + }, sub { + $mech->get_ok( '/report/' . $report->id ); + }; + } - $mech->get_ok('/around'); - $mech->content_contains( "Lichfield District Council FixMyStreet" ); - $mech->submit_form_ok( { with_fields => { pc => 'WS13 7RD' } }, "submit location" ); - $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); - $mech->submit_form_ok( - { - button => 'submit_register', - with_fields => { - title => 'Test Report', - detail => 'Test report details.', - photo => '', - name => 'Joe Bloggs', - may_show_name => '1', - email => $test_email, - phone => '07903 123 456', - category => 'Street lighting', + $report->discard_changes; + is $report->state, 'confirmed', "Report is now confirmed"; + + is $report->name, 'Joe Bloggs', 'name updated correctly'; + + $mech->delete_user($user); + }; + } +}; + +subtest "categories from deleted bodies shouldn't be visible for new reports" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/report/new/ajax?latitude=51.896268&longitude=-2.093063'); # Cheltenham + ok $mech->content_contains( $contact3->category ); + + # Delete the body which the contact belongs to. + $contact3->body->update( { deleted => 1 } ); + + $mech->get_ok('/report/new/ajax?latitude=51.896268&longitude=-2.093063'); # Cheltenham + ok $mech->content_lacks( $contact3->category ); + + $contact3->body->update( { deleted => 0 } ); + }; +}; + +subtest "unresponsive body handling works" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + # Test body-level send method + my $old_send = $contact1->body->send_method; + $contact1->body->update( { send_method => 'Refused' } ); + $mech->get_ok('/report/new/ajax?latitude=55.952055&longitude=-3.189579'); # Edinburgh + my $body_id = $contact1->body->id; + ok $mech->content_like( qr{Edinburgh.*accept reports.*/unresponsive\?body=$body_id} ); + + my $test_email = 'test-2@example.com'; + $mech->log_out_ok; + $mech->get_ok('/around'); + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $mech->submit_form_ok( + { + with_fields => { + title => "Test Report at café", + detail => 'Test report details.', + photo1 => '', + name => 'Joe Bloggs', + email => $test_email, + may_show_name => '1', + phone => '07903 123 456', + category => 'Trees', + } + }, + "submit good details" + ); + + my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + ok $user, "test user does exist"; + + my $report = $user->problems->first; + ok $report, "Found the report"; + is $report->bodies_str, undef, "Report not going anywhere"; + + like $mech->get_text_body_from_email, qr/despite not being sent/i, "correct email sent"; + + $user->problems->delete; + $mech->clear_emails_ok; + + # Make sure the same behaviour occurs for reports from the mobile app + $mech->log_out_ok; + $mech->post_ok( '/report/new/mobile', { + title => "Test Report at café", + detail => 'Test report details.', + photo1 => '', + name => 'Joe Bloggs', + email => $test_email, + may_show_name => '1', + phone => '07903 123 456', + category => 'Trees', + service => 'iOS', + lat => 55.952055, + lon => -3.189579, + pc => '', + used_map => '1', + submit_register => '1', + password_register => '', + }); + my $res = $mech->response; + ok $res->header('Content-Type') =~ m{^application/json\b}, 'response should be json'; + + $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + ok $user, "test user does exist"; + + $report = $user->problems->first; + ok $report, "Found the report"; + is $report->bodies_str, undef, "Report not going anywhere"; + + like $mech->get_text_body_from_email, qr/despite not being sent/i, "correct email sent"; + + $user->problems->delete; + $mech->clear_emails_ok; + + $contact1->body->update( { send_method => $old_send } ); + + # And test per-category refusing + my $old_email = $contact3->email; + $contact3->update( { email => 'REFUSED' } ); + $mech->get_ok('/report/new/category_extras?category=Trees&latitude=51.896268&longitude=-2.093063'); + ok $mech->content_like( qr/Cheltenham.*Trees.*unresponsive.*category=Trees/ ); + + $mech->get_ok('/around'); + $mech->submit_form_ok( { with_fields => { pc => 'GL50 2PR', } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $mech->submit_form_ok( + { + with_fields => { + title => "Test Report at café", + detail => 'Test report details.', + photo1 => '', + name => 'Joe Bloggs', + email => $test_email, + may_show_name => '1', + phone => '07903 123 456', + category => 'Trees', + } + }, + "submit good details" + ); + + $report = $user->problems->first; + ok $report, "Found the report"; + is $report->bodies_str, undef, "Report not going anywhere"; + + $contact3->update( { email => $old_email } ); + $mech->delete_user($user); + }; +}; + +subtest "unresponsive body page works" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + }, sub { + my $old_send = $contact1->body->send_method; + my $body_id = $contact1->body->id; + my $url = "/unresponsive?body=$body_id"; + is $mech->get($url)->code, 404, "page not found"; + $contact1->body->update( { send_method => 'Refused' } ); + $mech->get_ok($url); + $mech->content_contains('Edinburgh'); + $contact1->body->update( { send_method => $old_send } ); + + my $old_email = $contact3->email; + $body_id = $contact3->body->id; + $url = "/unresponsive?body=$body_id;category=Trees"; + is $mech->get($url)->code, 404, "page not found"; + $contact3->update( { email => 'REFUSED' } ); + $mech->get_ok($url); + $mech->content_contains('Cheltenham'); + $mech->content_contains('Trees'); + $contact3->update( { email => $old_email } ); + }; +}; + +subtest "extra google analytics code displayed on logged in problem creation" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + BASE_URL => 'https://www.fixmystreet.com', + MAPIT_URL => 'http://mapit.uk/', + }, sub { + # check that the user does not exist + my $test_email = 'test-2@example.com'; + + $mech->clear_emails_ok; + my $user = $mech->log_in_ok($test_email); + + # setup the user. + ok $user->update( + { + name => 'Test User', + phone => '01234 567 890', } - }, - "submit good details" - ); - is_deeply $mech->page_errors, [], "check there were no errors"; + ), + "set users details"; - # check that the user has been created/ not changed - my $user = - FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); - ok $user, "user found"; + # submit initial pc form + $mech->get_ok('/around'); + $mech->submit_form_ok( { with_fields => { pc => 'GL50 2PR', } }, + "submit location" ); - # find the report - my $report = $user->problems->first; - ok $report, "Found the report"; + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); - # Check the report has been assigned appropriately - is $report->council, 2240; + $mech->submit_form_ok( + { + with_fields => { + title => "Test Report at café", + detail => 'Test report details.', + photo1 => '', + name => 'Joe Bloggs', + may_show_name => '1', + phone => '07903 123 456', + category => 'Trees', + } + }, + "submit good details" + ); - # receive token - my $email = $mech->get_email; - ok $email, "got an email"; - like $email->body, qr/confirm the problem/i, "confirm the problem"; + # find the report + my $report = $user->problems->first; + ok $report, "Found the report"; - my ($url) = $email->body =~ m{(http://\S+)}; - ok $url, "extracted confirm url '$url'"; + $mech->content_contains( "'id': 'report/" . $report->id . "'", 'extra google code present' ); - # confirm token - $mech->get_ok($url); - $report->discard_changes; - is $report->state, 'confirmed', "Report is now confirmed"; + # cleanup + $mech->delete_user($user); + }; +}; - # Shouldn't be found, as it was a county problem - is $mech->get( '/report/' . $report->id )->code, 404, "report not found"; +subtest "extra google analytics code displayed on email confirmation problem creation" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + BASE_URL => 'https://www.fixmystreet.com', + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->log_out_ok; + $mech->clear_emails_ok; - # But should be on the main site - $mech->host( 'www.fixmystreet.com' ); - $mech->get_ok( '/report/' . $report->id ); - is $report->name, 'Joe Bloggs', 'name updated correctly'; + $mech->get_ok('/'); + $mech->submit_form_ok( { with_fields => { pc => 'GL50 2PR' } }, + "submit location" ); + $mech->follow_link_ok( + { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" + ); - $mech->delete_user($user); -} + my $fields = $mech->visible_form_values('mapSkippedForm'); + my $submission_fields = { + title => "Test Report", + detail => 'Test report details.', + photo1 => '', + email => 'firstlast@example.com', + name => 'Test User', + may_show_name => '1', + phone => '07903 123 456', + category => 'Trees', + password_register => '', + }; -$contact1->delete; -$contact2->delete; -$contact3->delete; -$contact4->delete; -$contact5->delete; -$contact6->delete; -$contact7->delete; + $mech->submit_form_ok( { with_fields => $submission_fields }, + "submit good details" ); + + my $email = $mech->get_email; + ok $email, "got an email"; + like $mech->get_text_body_from_email($email), qr/confirm that you want to/i, "confirm the problem"; + + my $url = $mech->get_link_from_email($email); + + # confirm token in order to update the user details + $mech->get_ok($url); + + # find the report + my $user = + FixMyStreet::App->model('DB::User') + ->find( { email => 'firstlast@example.com' } ); + + my $report = $user->problems->first; + ok $report, "Found the report"; + + $mech->content_contains( "'id': 'report/" . $report->id . "'", 'extra google code present' ); + + $user->problems->delete; + $user->alerts->delete; + $user->delete; + }; +}; done_testing(); + +END { + $mech->delete_body($_) foreach @bodies; +} diff --git a/t/app/controller/report_new_mobile.t b/t/app/controller/report_new_mobile.t new file mode 100644 index 000000000..3dfb99b2f --- /dev/null +++ b/t/app/controller/report_new_mobile.t @@ -0,0 +1,42 @@ +use Test::More; +use FixMyStreet::TestMech; +use LWP::Protocol::PSGI; +use t::Mock::MapItZurich; + +my $mech = FixMyStreet::TestMech->new; + +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + +LWP::Protocol::PSGI->register(t::Mock::MapItZurich->to_psgi_app, host => 'mapit.zurich'); + +subtest "Check signed up for alert when logged in" => sub { + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.zurich', + MAPIT_TYPES => [ 'O08' ], + }, sub { + $mech->log_in_ok('user@example.org'); + $mech->post_ok( '/report/new/mobile', { + service => 'iPhone', + title => 'Title', + detail => 'Problem detail', + lat => 47.381817, + lon => 8.529156, + email => 'user@example.org', + pc => '', + name => 'Name', + }); + my $res = $mech->response; + ok $res->header('Content-Type') =~ m{^application/json\b}, 'response should be json'; + + my $user = FixMyStreet::DB->resultset('User')->search({ email => 'user@example.org' })->first; + my $a = FixMyStreet::DB->resultset('Alert')->search({ user_id => $user->id })->first; + isnt $a, undef, 'User is signed up for alert'; + }; +}; + +END { + $mech->delete_user('user@example.org'); + done_testing(); +} diff --git a/t/app/controller/report_new_open311.t b/t/app/controller/report_new_open311.t index e9ada9f41..e3a464f88 100644 --- a/t/app/controller/report_new_open311.t +++ b/t/app/controller/report_new_open311.t @@ -3,44 +3,46 @@ use warnings; use Test::More; use FixMyStreet::TestMech; +use FixMyStreet::App; use Web::Scraper; +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + my $mech = FixMyStreet::TestMech->new; -FixMyStreet::App->model('DB::Body')->find_or_create( { - area_id => 2651, - endpoint => 'http://example.com/open311', - jurisdiction => 'mySociety', - api_key => 'apikey', -} ); - -my %contact_params = ( - confirmed => 1, - deleted => 0, - editor => 'Test', - whenedited => \'current_timestamp', - note => 'Created for test', -); +my $body = $mech->create_body_ok(2245, 'Wiltshire Council'); +$body->update({ + endpoint => 'http://example.com/open311', + jurisdiction => 'mySociety', + api_key => 'apikey', +}); + # Let's make some contacts to send things to! -my $contact1 = FixMyStreet::App->model('DB::Contact')->find_or_create( { - %contact_params, - body_id => 2651, # Edinburgh +my $contact1 = $mech->create_contact_ok( + body_id => $body->id, # Edinburgh category => 'Street lighting', email => '100', extra => [ { description => 'Lamppost number', code => 'number', required => 'True' }, { description => 'Lamppost type', code => 'type', required => 'False', values => { value => [ { name => ['Gas'], key => ['old'] }, { name => [ 'Yellow' ], key => [ 'modern' ] } ] } - } + } + ], +); +my $contact1b = $mech->create_contact_ok( + body_id => $body->id, # Edinburgh + category => 'Moon lighting', + email => '100b', + extra => [ { description => 'Moon type', code => 'type', required => 'False', values => + [ { name => 'Full', key => 'full' }, { name => 'New', key => 'new' } ] } ], -} ); -my $contact2 = FixMyStreet::App->model('DB::Contact')->find_or_create( { - %contact_params, - body_id => 2651, # Edinburgh +); +my $contact2 = $mech->create_contact_ok( + body_id => $body->id, # Edinburgh category => 'Graffiti Removal', email => '101', -} ); -ok $contact1, "created test contact 1"; -ok $contact2, "created test contact 2"; +); # test that the various bit of form get filled in and errors correctly # generated. @@ -51,7 +53,9 @@ foreach my $test ( fields => { title => '', detail => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', name => '', may_show_name => '1', email => '', @@ -66,9 +70,9 @@ foreach my $test ( type => 'old', }, errors => [ + 'This information is required', 'Please enter a subject', 'Please enter some details', - 'This information is required', 'Please enter your email', 'Please enter your name', ], @@ -111,17 +115,22 @@ foreach my $test ( $mech->get_ok('/around'); # submit initial pc form - $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, - "submit location" ); - is_deeply $mech->page_errors, [], "no errors for pc '$test->{pc}'"; - - # click through to the report page - $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, - "follow 'skip this step' link" ); - - # submit the main form - $mech->submit_form_ok( { with_fields => $test->{fields} }, - "submit form" ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, + "submit location" ); + is_deeply $mech->page_errors, [], "no errors for pc '$test->{pc}'"; + + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); + + # submit the main form + $mech->submit_form_ok( { with_fields => $test->{fields} }, + "submit form" ); + }; # check that we got the errors expected is_deeply $mech->page_errors, $test->{errors}, "check errors"; @@ -147,21 +156,27 @@ foreach my $test ( %{ $test->{fields} }, %{ $test->{submit_with} }, }; - $mech->submit_form_ok( { with_fields => $new_values } ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => $new_values } ); + }; $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); ok $user, 'created user'; my $prob = $user->problems->first; ok $prob, 'problem created'; - is_deeply $prob->extra, $test->{extra}, 'extra open311 data added to problem'; + is_deeply $prob->get_extra_fields, $test->{extra}, 'extra open311 data added to problem'; $user->problems->delete; $user->delete; }; } -$contact1->delete; -$contact2->delete; - done_testing(); + +END { + $mech->delete_body($body); +} diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t index 2c7294222..de153978b 100644 --- a/t/app/controller/report_updates.t +++ b/t/app/controller/report_updates.t @@ -13,15 +13,11 @@ my $mech = FixMyStreet::TestMech->new; $mech->delete_user('commenter@example.com'); $mech->delete_user('test@example.com'); -my $user = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'test@example.com', name => 'Test User' } ); -ok $user, "created test user"; +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); -my $user2 = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'commenter@example.com', name => 'Commenter' } ); -ok $user2, "created comment user"; +my $user2 = $mech->create_user_ok('commenter@example.com', name => 'Commenter'); + +my $body = $mech->create_body_ok(2504, 'Westminster City Council'); my $dt = DateTime->new( year => 2011, @@ -35,7 +31,7 @@ my $dt = DateTime->new( my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { postcode => 'SW1A 1AA', - council => '2504', + bodies_str => $body->id, areas => ',105255,11806,11828,2247,2504,', category => 'Other', title => 'Test 2', @@ -135,6 +131,19 @@ for my $test ( }; } +subtest "updates displayed on report with empty bodies_str" => sub { + my $old_bodies_str = $report->bodies_str; + $report->update({ bodies_str => undef }); + $comment->update({ problem_state => 'fixed' , mark_open => 'false', mark_fixed => 'false' }); + + $mech->get_ok("/report/$report_id"); + + my $meta = $mech->extract_update_metas; + is scalar @$meta, 1, 'update displayed'; + + $report->update({ bodies_str => $old_bodies_str }); +}; + subtest "unconfirmed updates not displayed" => sub { $comment->state( 'unconfirmed' ); $comment->update; @@ -197,7 +206,9 @@ for my $test ( rznvy => '', update => '', name => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', fixed => undef, add_alert => 1, may_show_name => undef, @@ -214,7 +225,9 @@ for my $test ( rznvy => 'test', update => '', name => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', fixed => undef, add_alert => 1, may_show_name => undef, @@ -231,7 +244,9 @@ for my $test ( rznvy => 'test @ example. com', update => '', name => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', fixed => undef, add_alert => 1, may_show_name => undef, @@ -250,7 +265,9 @@ for my $test ( rznvy => 'test@EXAMPLE.COM', update => '', name => '', - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', fixed => undef, add_alert => 1, may_show_name => undef, @@ -290,7 +307,9 @@ for my $test ( rznvy => '', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', fixed => undef, remember_me => undef, @@ -314,7 +333,9 @@ for my $test ( rznvy => '', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', fixed => undef, remember_me => undef, @@ -351,13 +372,14 @@ for my $test ( 'submit update' ); - $mech->content_contains('Nearly Done! Now check your email'); + $mech->content_contains('Nearly done! Now check your email'); my $email = $mech->get_email; - ok $email, "got an email"; - like $email->body, qr/confirm the update you/i, "Correct email text"; + my $body = $mech->get_text_body_from_email($email); + like $body, qr/confirm your update on/i, "Correct email text"; - my ( $url, $url_token ) = $email->body =~ m{(http://\S+/C/)(\S+)}; + my $url = $mech->get_link_from_email($email); + my ($url_token) = $url =~ m{/C/(\S+)}; ok $url, "extracted confirm url '$url'"; my $token = FixMyStreet::App->model('DB::Token')->find( @@ -384,7 +406,7 @@ for my $test ( is $update->text, $details->{update}, 'update text'; is $add_alerts, $details->{add_alert} ? 1 : 0, 'do not sign up for alerts'; - $mech->get_ok( $url . $url_token ); + $mech->get_ok( $url ); $mech->content_contains("/report/$report_id#update_$update_id"); my $unreg_user = FixMyStreet::App->model( 'DB::User' )->find( { email => $details->{rznvy} } ); @@ -407,24 +429,103 @@ for my $test ( $report->state('confirmed'); $report->update; +for my $test ( + { + desc => 'overriding email confirmation allows report confirmation with no email sent', + initial_values => { + name => '', + rznvy => '', + may_show_name => 1, + add_alert => 1, + photo1 => '', + photo2 => '', + photo3 => '', + update => '', + fixed => undef, + remember_me => undef, + password_register => '', + password_sign_in => '', + }, + form_values => { + submit_update => 1, + rznvy => 'unregistered@example.com', + update => "update no email confirm", + add_alert => 1, + name => 'Unreg User', + may_show_name => undef, + }, + changes => { + update => "Update no email confirm", + }, + } +) { + subtest $test->{desc} => sub { + my $send_confirmation_mail_override = Sub::Override->new( + "FixMyStreet::Cobrand::Default::never_confirm_updates", + sub { return 1; } + ); + $mech->log_out_ok(); + $mech->clear_emails_ok(); + + $mech->get_ok("/report/$report_id"); + + my $values = $mech->visible_form_values('updateForm'); + + is_deeply $values, $test->{initial_values}, 'initial form values'; + + $mech->submit_form_ok( + { + with_fields => $test->{form_values} + }, + 'submit update' + ); + $mech->content_contains("/report/$report_id"); + $mech->get_ok("/report/$report_id"); + + $mech->content_contains('Test 2'); + $mech->content_contains('Update no email confirm'); + + my $email = $mech->email_count_is(0); + + my $update = + FixMyStreet::App->model('DB::Comment')->find( { problem_id => $report_id, text => 'Update no email confirm' } ); + my $update_id = $update->id; + + $mech->content_contains('name="update_' . $update_id . '"'); + + my $details = { + %{ $test->{form_values} }, + %{ $test->{changes} } + }; + + ok $update, 'found update in database'; + is $update->state, 'confirmed', 'update confirmed'; + is $update->user->email, $details->{rznvy}, 'update email'; + is $update->text, $details->{update}, 'update text'; + + my $unreg_user = FixMyStreet::App->model( 'DB::User' )->find( { email => $details->{rznvy} } ); + + ok $unreg_user, 'found user'; + + $mech->delete_user( $unreg_user ); + $send_confirmation_mail_override->restore(); + }; +} + subtest 'check non authority user cannot change set state' => sub { $mech->log_in_ok( $user->email ); - $user->from_body( 0 ); + $user->from_body( undef ); $user->update; $mech->get_ok("/report/$report_id"); - $mech->post_ok( "/report/update", { - submit_update => 1, - id => $report_id, - name => $user->name, - may_show_name => 1, - add_alert => undef, - photo => '', - update => 'this is a forbidden update', - state => 'fixed - council', + $mech->submit_form_ok( { + form_id => 'form_update_form', + fields => { + may_show_name => 1, + update => 'this is a forbidden update', + state => 'fixed - council', }, - 'submitted with state', - ); + }, 'submitted with state'); is $mech->uri->path, "/report/update", "at /report/update"; @@ -437,22 +538,18 @@ subtest 'check non authority user cannot change set state' => sub { for my $state ( qw/unconfirmed hidden partial/ ) { subtest "check that update cannot set state to $state" => sub { $mech->log_in_ok( $user->email ); - $user->from_body( 2504 ); + $user->from_body( $body->id ); $user->update; $mech->get_ok("/report/$report_id"); - $mech->post_ok( "/report/update", { - submit_update => 1, - id => $report_id, - name => $user->name, - may_show_name => 1, - add_alert => undef, - photo => '', - update => 'this is a forbidden update', - state => $state, + $mech->submit_form_ok( { + form_id => 'form_update_form', + fields => { + may_show_name => 1, + update => 'this is a forbidden update', + state => $state, }, - 'submitted with state', - ); + }, 'submitted with state'); is $mech->uri->path, "/report/update", "at /report/update"; @@ -469,96 +566,136 @@ for my $test ( fields => { name => $user->name, may_show_name => 1, - add_alert => undef, - photo => '', update => 'Set state to investigating', state => 'investigating', }, state => 'investigating', }, { - desc => 'from authority user marks report as planned', + desc => 'from authority user marks report as in progress', fields => { name => $user->name, may_show_name => 1, - add_alert => undef, - photo => '', - update => 'Set state to planned', - state => 'planned', + update => 'Set state to in progress', + state => 'in progress', + }, + state => 'in progress', + }, + { + desc => 'from authority user marks report as fixed', + fields => { + name => $user->name, + may_show_name => 1, + update => 'Set state to fixed', + state => 'fixed', }, - state => 'planned', + state => 'fixed - council', }, { - desc => 'from authority user marks report as in progress', + desc => 'from authority user marks report as action scheduled', fields => { name => $user->name, may_show_name => 1, - add_alert => undef, - photo => '', - update => 'Set state to in progress', - state => 'in progress', + update => 'Set state to action scheduled', + state => 'action scheduled', }, - state => 'in progress', + state => 'action scheduled', }, { - desc => 'from authority user marks report as closed', + desc => 'from authority user marks report as unable to fix', fields => { name => $user->name, may_show_name => 1, - add_alert => undef, - photo => '', - update => 'Set state to closed', - state => 'closed', + update => 'Set state to unable to fix', + state => 'no further action', }, - state => 'closed', + state => 'unable to fix', }, { - desc => 'from authority user marks report as fixed', + desc => 'from authority user marks report as internal referral', fields => { name => $user->name, may_show_name => 1, - add_alert => undef, - photo => '', - update => 'Set state to fixed', - state => 'fixed', + update => 'Set state to internal referral', + state => 'internal referral', }, - state => 'fixed - council', + state => 'internal referral', + meta => "an internal referral", }, { - desc => 'from authority user marks report as confirmed', + desc => 'from authority user marks report as not responsible', fields => { name => $user->name, may_show_name => 1, - add_alert => undef, - photo => '', - update => 'Set state to confirmed', - state => 'confirmed', + update => 'Set state to not responsible', + state => 'not responsible', + }, + state => 'not responsible', + meta => "not the council's responsibility" + }, + { + desc => 'from authority user marks report as duplicate', + fields => { + name => $user->name, + may_show_name => 1, + update => 'Set state to duplicate', + state => 'duplicate', + }, + state => 'duplicate', + meta => 'a duplicate report', + }, + { + desc => 'from authority user marks report as internal referral', + fields => { + name => $user->name, + may_show_name => 1, + update => 'Set state to internal referral', + state => 'internal referral', }, - state => 'confirmed', + state => 'internal referral', + meta => 'an internal referral', }, { desc => 'from authority user marks report sent to two councils as fixed', fields => { name => $user->name, may_show_name => 1, - add_alert => undef, - photo => '', update => 'Set state to fixed', state => 'fixed', }, state => 'fixed - council', - report_councils => '2504,2505', + report_bodies => $body->id . ',2505', + }, + { + desc => 'from authority user show username for users with correct permissions', + fields => { + name => $user->name, + may_show_name => 1, + update => 'Set state to fixed', + state => 'fixed', + }, + state => 'fixed - council', + report_bodies => $body->id . ',2505', + view_username => 1 }, ) { subtest $test->{desc} => sub { $report->comments->delete; - if ( $test->{ report_councils } ) { - $report->council( $test->{ report_councils } ); + if ( $test->{ report_bodies } ) { + $report->bodies_str( $test->{ report_bodies } ); $report->update; } $mech->log_in_ok( $user->email ); - $user->from_body( 2504 ); + + if ($test->{view_username}) { + ok $user->user_body_permissions->create({ + body => $body, + permission_type => 'view_body_contribute_details' + }), 'Give user view_body_contribute_details permissions'; + } + + $user->from_body( $body->id ); $user->update; $mech->get_ok("/report/$report_id"); @@ -569,6 +706,7 @@ for my $test ( }, 'submit update' ); + $mech->get_ok("/report/$report_id"); $report->discard_changes; my $update = $report->comments->first; @@ -577,14 +715,22 @@ for my $test ( is $update->problem_state, $test->{state}, 'problem state set'; my $update_meta = $mech->extract_update_metas; - # setting it to confirmed shouldn't say anything - if ( $test->{fields}->{state} ne 'confirmed' ) { - like $update_meta->[0], qr/marked as $test->{fields}->{state}$/, 'update meta includes state change'; + my $meta_state = $test->{meta} || $test->{fields}->{state}; + if ( $test->{reopened} ) { + like $update_meta->[0], qr/reopened$/, 'update meta says reopened'; + } elsif ( $test->{state} eq 'duplicate' ) { + like $update_meta->[0], qr/closed as $meta_state$/, 'update meta includes state change'; } else { - like $update_meta->[0], qr/reopened$/, 'update meta includes state change'; + like $update_meta->[0], qr/marked as $meta_state$/, 'update meta includes state change'; + } + + if ($test->{view_username}) { + like $update_meta->[0], qr{Westminster City Council \(Test User\)}, 'update meta includes council and user name'; + $user->user_body_permissions->delete_all; + } else { + like $update_meta->[0], qr{Westminster City Council}, 'update meta includes council name'; + $mech->content_contains( '<strong>Westminster City Council</strong>', 'council name in bold'); } - like $update_meta->[0], qr{Test User \(Westminster City Council\)}, 'update meta includes council name'; - $mech->content_contains( 'Test User (<strong>Westminster City Council</strong>)', 'council name in bold'); $report->discard_changes; is $report->state, $test->{state}, 'state set'; @@ -598,7 +744,8 @@ subtest 'check meta correct for comments marked confirmed but not marked open' = user => $user, problem_id => $report->id, text => 'update text', - confirmed => DateTime->now, + # Subtract a day to deal with any code/db timezone difference + confirmed => DateTime->now( time_zone => 'local' ) - DateTime::Duration->new( days => 1 ), problem_state => 'confirmed', anonymous => 0, mark_open => 0, @@ -609,7 +756,7 @@ subtest 'check meta correct for comments marked confirmed but not marked open' = $mech->get_ok( "/report/" . $report->id ); my $update_meta = $mech->extract_update_metas; - like $update_meta->[0], qr/reopened$/, + unlike $update_meta->[0], qr/reopened$/, 'update meta does not say reopened'; $comment->update( { mark_open => 1, problem_state => undef } ); @@ -627,13 +774,170 @@ subtest 'check meta correct for comments marked confirmed but not marked open' = unlike $update_meta->[0], qr/marked as open$/, 'update meta does not says marked as open'; unlike $update_meta->[0], qr/reopened$/, 'update meta does not say reopened'; - }; +}; + +subtest "check first comment with no status change has no status in meta" => sub { + $mech->log_in_ok( $user->email ); + $user->from_body( undef ); + $user->update; + + my $comment = $report->comments->first; + $comment->update( { mark_fixed => 0, problem_state => 'confirmed' } ); + + $mech->get_ok("/report/$report_id"); + + my $update_meta = $mech->extract_update_metas; + unlike $update_meta->[0], qr/marked as|reopened/, 'update meta does not include state change'; +}; + +subtest "check comment with no status change has not status in meta" => sub { + $mech->log_in_ok( $user->email ); + $user->from_body( undef ); + $user->update; + + my $comment = $report->comments->first; + $comment->update( { mark_fixed => 1, problem_state => 'fixed - council' } ); + + $mech->get_ok("/report/$report_id"); + + $mech->submit_form_ok( + { + with_fields => { + name => $user->name, + may_show_name => 1, + add_alert => undef, + photo1 => '', + photo2 => '', + photo3 => '', + update => 'Comment that does not change state', + }, + }, + 'submit update' + ); + $mech->get_ok("/report/$report_id"); + + $report->discard_changes; + my @updates = $report->comments->all; + is scalar @updates, 2, 'correct number of updates'; + + my $update = pop @updates; + + is $report->state, 'fixed - council', 'correct report state'; + is $update->problem_state, 'fixed - council', 'correct update state'; + my $update_meta = $mech->extract_update_metas; + unlike $update_meta->[1], qr/marked as/, 'update meta does not include state change'; + + $user->from_body( $body->id ); + $user->update; + + $mech->get_ok("/report/$report_id"); + + $mech->submit_form_ok( + { + with_fields => { + name => $user->name, + may_show_name => 1, + add_alert => undef, + photo1 => '', + photo2 => '', + photo3 => '', + update => 'Comment that sets state to investigating', + state => 'investigating', + }, + }, + 'submit update' + ); + $mech->get_ok("/report/$report_id"); + + $report->discard_changes; + @updates = $report->comments->search(undef, { order_by => 'created' })->all;; + + is scalar @updates, 3, 'correct number of updates'; + + $update = pop @updates; + + is $report->state, 'investigating', 'correct report state'; + is $update->problem_state, 'investigating', 'correct update state'; + $update_meta = $mech->extract_update_metas; + like $update_meta->[0], qr/marked as fixed/, 'first update meta says fixed'; + unlike $update_meta->[1], qr/marked as/, 'second update meta does not include state change'; + like $update_meta->[2], qr/marked as investigating/, 'third update meta says investigating'; + + my $dt = DateTime->now( time_zone => "local" )->add( seconds => 1 ); + $comment = FixMyStreet::App->model('DB::Comment')->find_or_create( + { + problem_id => $report_id, + user_id => $user->id, + name => 'Other User', + mark_fixed => 'false', + text => 'This is some update text', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + anonymous => 'f', + } + ); + + $mech->get_ok("/report/$report_id"); + + $report->discard_changes; + @updates = $report->comments->search(undef, { order_by => 'created' })->all;; + is scalar @updates, 4, 'correct number of updates'; + + $update = pop @updates; + + is $report->state, 'investigating', 'correct report state'; + is $update->problem_state, undef, 'no update state'; + $update_meta = $mech->extract_update_metas; + like $update_meta->[0], qr/marked as fixed/, 'first update meta says fixed'; + unlike $update_meta->[1], qr/marked as/, 'second update meta does not include state change'; + like $update_meta->[2], qr/marked as investigating/, 'third update meta says investigating'; + unlike $update_meta->[3], qr/marked as/, 'fourth update meta has no state change'; +}; + +subtest 'check meta correct for second comment marking as reopened' => sub { + $report->comments->delete; + my $comment = FixMyStreet::App->model('DB::Comment')->create( + { + user => $user, + problem_id => $report->id, + text => 'update text', + confirmed => DateTime->now( time_zone => 'local'), + problem_state => 'fixed - user', + anonymous => 0, + mark_open => 0, + mark_fixed => 1, + state => 'confirmed', + } + ); + + $mech->get_ok( "/report/" . $report->id ); + my $update_meta = $mech->extract_update_metas; + like $update_meta->[0], qr/fixed$/, 'update meta says fixed'; -$user->from_body(0); + $comment = FixMyStreet::App->model('DB::Comment')->create( + { + user => $user, + problem_id => $report->id, + text => 'update text', + confirmed => DateTime->now( time_zone => 'local' ) + DateTime::Duration->new( minutes => 1 ), + problem_state => 'confirmed', + anonymous => 0, + mark_open => 0, + mark_fixed => 0, + state => 'confirmed', + } + ); + + $mech->get_ok( "/report/" . $report->id ); + $update_meta = $mech->extract_update_metas; + like $update_meta->[1], qr/reopened$/, 'update meta says reopened'; +}; + +$user->from_body(undef); $user->update; $report->state('confirmed'); -$report->council('2504'); +$report->bodies_str($body->id); $report->update; for my $test ( @@ -660,9 +964,7 @@ for my $test ( add_alert => undef, password_sign_in => 'secret2', }, - field_errors => [ - 'You have successfully signed in; please check and confirm your details are accurate:', - ], + message => 'You have successfully signed in; please check and confirm your details are accurate:', } ) { subtest $test->{desc} => sub { @@ -683,7 +985,10 @@ for my $test ( 'submit update' ); - is_deeply $mech->page_errors, $test->{field_errors}, 'check there were errors'; + $mech->content_contains($test->{message}) if $test->{message}; + + is_deeply $mech->page_errors, $test->{field_errors}, 'check there were errors' + if $test->{field_errors}; SKIP: { skip( "Incorrect password", 5 ) unless $test->{form_values}{password_sign_in} eq $pw; @@ -698,7 +1003,7 @@ for my $test ( "submit good details" ); - is $mech->uri->path, "/report/" . $report_id, "redirected to report page"; + $mech->content_contains('Thank you for updating this issue'); $mech->email_count_is(0); my $update = $report->comments->first; @@ -729,7 +1034,7 @@ subtest 'submit an update for a registered user, creating update by email' => su }, }, 'submit update' ); - $mech->content_contains('Nearly Done! Now check your email'); + $mech->content_contains('Nearly done! Now check your email'); # No change to user yet. $user->discard_changes; @@ -737,10 +1042,11 @@ subtest 'submit an update for a registered user, creating update by email' => su is $user->name, 'Mr Reg', 'name unchanged'; my $email = $mech->get_email; - ok $email, "got an email"; - like $email->body, qr/confirm the update you/i, "Correct email text"; + my $body = $mech->get_text_body_from_email($email); + like $body, qr/confirm your update on/i, "Correct email text"; - my ( $url, $url_token ) = $email->body =~ m{(http://\S+/C/)(\S+)}; + my $url = $mech->get_link_from_email($email); + my ($url_token) = $url =~ m{/C/(\S+)}; ok $url, "extracted confirm url '$url'"; my $token = FixMyStreet::App->model('DB::Token')->find( { @@ -758,7 +1064,7 @@ subtest 'submit an update for a registered user, creating update by email' => su is $update->user->email, 'registered@example.com', 'update email'; is $update->text, 'Update from a user', 'update text'; - $mech->get_ok( $url . $url_token ); + $mech->get_ok( $url ); $mech->content_contains("/report/$report_id#update_$update_id"); # User should have new name and password @@ -771,6 +1077,9 @@ subtest 'submit an update for a registered user, creating update by email' => su $mech->delete_user( $user ); }; +my $sample_file = file(__FILE__)->parent->file("sample.jpg")->stringify; +ok -e $sample_file, "sample file $sample_file exists"; + for my $test ( { desc => 'submit update for registered user', @@ -778,7 +1087,9 @@ for my $test ( name => 'Test User', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', fixed => undef, }, @@ -788,6 +1099,7 @@ for my $test ( update => 'update from a registered user', add_alert => undef, fixed => undef, + photo1 => [ [ $sample_file, undef, Content_Type => 'image/jpeg' ], 1 ], }, changed => { update => 'Update from a registered user' @@ -803,7 +1115,9 @@ for my $test ( name => 'Test User', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', fixed => undef, }, @@ -829,7 +1143,9 @@ for my $test ( name => 'Test User', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', fixed => undef, }, @@ -854,7 +1170,9 @@ for my $test ( name => 'Commenter', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', fixed => undef, }, @@ -879,7 +1197,9 @@ for my $test ( name => 'Commenter', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', }, email => 'commenter@example.com', @@ -928,7 +1248,14 @@ for my $test ( 'submit update' ); - is $mech->uri->path, "/report/" . $report_id, "redirected to report page"; + $mech->content_contains('Thank you for updating this issue'); + $mech->content_contains("/report/" . $report_id); + $mech->get_ok("/report/" . $report_id); + + my $update = $report->comments->first; + ok $update, 'found update'; + + $mech->content_contains("/photo/c/" . $update->id . ".0.jpeg") if $test->{fields}->{photo1}; if ( !defined( $test->{endstate_banner} ) ) { is $mech->extract_problem_banner->{text}, undef, 'endstate banner'; @@ -943,8 +1270,6 @@ for my $test ( %{ $test->{changed} }, }; - my $update = $report->comments->first; - ok $update, 'found update'; is $update->text, $results->{update}, 'update text'; is $update->user->email, $test->{email}, 'update user'; is $update->state, 'confirmed', 'update confirmed'; @@ -965,7 +1290,9 @@ foreach my $test ( name => 'Test User', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', fixed => undef, }, @@ -982,7 +1309,6 @@ foreach my $test ( alert => 1, # we signed up for alerts before, do not unsign us anonymous => 0, answered => 0, - path => '/report/update', content => "Thanks, glad to hear it's been fixed! Could we just ask if you have ever reported a problem to a council before?", }, @@ -992,7 +1318,9 @@ foreach my $test ( name => 'Test User', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', fixed => undef, }, @@ -1009,7 +1337,6 @@ foreach my $test ( alert => 1, # we signed up for alerts before, do not unsign us anonymous => 0, answered => 0, - path => '/report/update', content => "Thanks, glad to hear it's been fixed! Could we just ask if you have ever reported a problem to a council before?", }, @@ -1020,7 +1347,9 @@ foreach my $test ( name => 'Test User', may_show_name => 1, add_alert => 1, - photo => '', + photo1 => '', + photo2 => '', + photo3 => '', update => '', fixed => undef, }, @@ -1037,7 +1366,6 @@ foreach my $test ( alert => 1, # we signed up for alerts before, do not unsign us anonymous => 0, answered => 1, - path => '/report/' . $report->id, content => $report->title, }, ) @@ -1062,7 +1390,7 @@ foreach my $test ( { problem_id => $report_id, ever_reported => 'y', - whensent => \'ms_current_timestamp()', + whensent => \'current_timestamp', } ); @@ -1090,7 +1418,7 @@ foreach my $test ( $mech->submit_form_ok( { with_fields => $test->{fields}, }, 'submit update' ); - is $mech->uri->path, $test->{path}, "page after submission"; + is $mech->uri->path, '/report/update', "page after submission"; $mech->content_contains( $test->{content} ); @@ -1120,7 +1448,8 @@ foreach my $test ( $mech->submit_form_ok( { with_fields => { reported => 'Yes' } } ); - $mech->content_contains( 'Thank you — you can' ); + $mech->content_contains( $report->title ); + $mech->content_contains( 'Thank you for updating this issue' ); $questionnaire = FixMyStreet::App->model( 'DB::Questionnaire' )->find( { problem_id => $report_id } @@ -1179,7 +1508,7 @@ for my $test ( anonymous => 0, answered => 1, path => '/report/update', - content => "You have successfully confirmed your update", + content => "Thank you for updating this issue", }, ) { @@ -1203,7 +1532,7 @@ for my $test ( { problem_id => $report_id, ever_reported => 'y', - whensent => \'ms_current_timestamp()', + whensent => \'current_timestamp', } ); @@ -1228,8 +1557,6 @@ for my $test ( $mech->content_contains( 'Now check your email' ); - $mech->email_count_is(1); - my $results = { %{ $test->{fields} }, %{ $test->{changed} }, }; my $update = $report->comments->first; @@ -1240,10 +1567,11 @@ for my $test ( is $update->anonymous, $test->{anonymous}, 'user anonymous'; my $email = $mech->get_email; - ok $email, "got an email"; - like $email->body, qr/confirm the update you/i, "Correct email text"; + my $body = $mech->get_text_body_from_email($email); + like $body, qr/confirm your update on/i, "Correct email text"; - my ( $url, $url_token ) = $email->body =~ m{(http://\S+/C/)(\S+)}; + my $url = $mech->get_link_from_email($email); + my ($url_token) = $url =~ m{/C/(\S+)}; ok $url, "extracted confirm url '$url'"; my $token = FixMyStreet::App->model('DB::Token')->find( @@ -1269,7 +1597,8 @@ for my $test ( $mech->submit_form_ok( { with_fields => { reported => 'Yes' } } ); - $mech->content_contains( 'Thank you — you can' ); + $mech->content_contains( $report->title ); + $mech->content_contains( 'Thank you for updating this issue' ); $questionnaire = FixMyStreet::App->model( 'DB::Questionnaire' )->find( { problem_id => $report_id } @@ -1286,10 +1615,261 @@ for my $test ( }; } +for my $test ( + { + desc => 'update confirmed without marking as fixed leaves state unchanged', + initial_state => 'confirmed', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 0, + }, + end_state => 'confirmed', + }, + { + desc => 'update investigating without marking as fixed leaves state unchanged', + initial_state => 'investigating', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 0, + }, + end_state => 'investigating', + }, + { + desc => 'update in progress without marking as fixed leaves state unchanged', + initial_state => 'in progress', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 0, + }, + end_state => 'in progress', + }, + { + desc => 'update action scheduled without marking as fixed leaves state unchanged', + initial_state => 'action scheduled', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 0, + }, + end_state => 'action scheduled', + }, + { + desc => 'update fixed without marking as open leaves state unchanged', + initial_state => 'fixed', + expected_form_fields => { + reopen => undef, + }, + submitted_form_fields => { + reopen => 0, + }, + end_state => 'fixed', + }, + { + desc => 'update unable to fix without marking as fixed leaves state unchanged', + initial_state => 'unable to fix', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 0, + }, + end_state => 'unable to fix', + }, + { + desc => 'update internal referral without marking as fixed leaves state unchanged', + initial_state => 'internal referral', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 0, + }, + end_state => 'internal referral', + }, + { + desc => 'update not responsible without marking as fixed leaves state unchanged', + initial_state => 'not responsible', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 0, + }, + end_state => 'not responsible', + }, + { + desc => 'update duplicate without marking as fixed leaves state unchanged', + initial_state => 'duplicate', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 0, + }, + end_state => 'duplicate', + }, + { + desc => 'can mark confirmed as fixed', + initial_state => 'confirmed', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 1, + }, + end_state => 'fixed - user', + }, + { + desc => 'can mark investigating as fixed', + initial_state => 'investigating', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 1, + }, + end_state => 'fixed - user', + }, + { + desc => 'can mark in progress as fixed', + initial_state => 'in progress', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 1, + }, + end_state => 'fixed - user', + }, + { + desc => 'can mark action scheduled as fixed', + initial_state => 'action scheduled', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 1, + }, + end_state => 'fixed - user', + }, + { + desc => 'cannot mark fixed as fixed, can mark as not fixed', + initial_state => 'fixed', + expected_form_fields => { + reopen => undef, + }, + submitted_form_fields => { + reopen => 1, + }, + end_state => 'confirmed', + }, + { + desc => 'can mark unable to fix as fixed, cannot mark not closed', + initial_state => 'unable to fix', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 1, + }, + end_state => 'fixed - user', + }, + { + desc => 'can mark internal referral as fixed, cannot mark not closed', + initial_state => 'internal referral', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 1, + }, + end_state => 'fixed - user', + }, + { + desc => 'can mark not responsible as fixed, cannot mark not closed', + initial_state => 'not responsible', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 1, + }, + end_state => 'fixed - user', + }, + { + desc => 'can mark duplicate as fixed, cannot mark not closed', + initial_state => 'duplicate', + expected_form_fields => { + fixed => undef, + }, + submitted_form_fields => { + fixed => 1, + }, + end_state => 'fixed - user', + }, +) { + subtest $test->{desc} => sub { + $mech->log_in_ok( $report->user->email ); + + my %standard_fields = ( + name => $report->user->name, + update => 'update text', + photo1 => '', + photo2 => '', + photo3 => '', + may_show_name => 1, + add_alert => 1, + ); + + my %expected_fields = ( + %standard_fields, + %{ $test->{expected_form_fields} }, + update => '', + ); + + my %submitted_fields = ( + %standard_fields, + %{ $test->{submitted_form_fields} }, + ); + + # clear out comments for this problem to make + # checking details easier later + ok( $_->delete, 'deleted comment ' . $_->id ) for $report->comments; + + $report->discard_changes; + $report->state($test->{initial_state}); + $report->update; + + $mech->get_ok("/report/$report_id"); + + my $values = $mech->visible_form_values('updateForm'); + is_deeply $values, \%expected_fields, 'correct form fields present'; + + if ( $test->{submitted_form_fields} ) { + $mech->submit_form_ok( { + with_fields => \%submitted_fields + }, + 'submit update' + ); + + $report->discard_changes; + is $report->state, $test->{end_state}, 'update sets correct report state'; + } + }; +} + subtest 'check have to be logged in for creator fixed questionnaire' => sub { $mech->log_out_ok(); - $mech->get_ok( "/questionnaire/submit?problem=$report_id&reported=Yes" ); + $mech->get( "/questionnaire/submit?problem=$report_id&reported=Yes" ); + is $mech->res->code, 400, "got 400"; $mech->content_contains( "I'm afraid we couldn't locate your problem in the database." ) }; @@ -1298,7 +1878,8 @@ subtest 'check cannot answer other user\'s creator fixed questionnaire' => sub { $mech->log_out_ok(); $mech->log_in_ok( $user2->email ); - $mech->get_ok( "/questionnaire/submit?problem=$report_id&reported=Yes" ); + $mech->get( "/questionnaire/submit?problem=$report_id&reported=Yes" ); + is $mech->res->code, 400, "got 400"; $mech->content_contains( "I'm afraid we couldn't locate your problem in the database." ) }; diff --git a/t/app/controller/reports.t b/t/app/controller/reports.t index a4dab6597..a21d3ad65 100644 --- a/t/app/controller/reports.t +++ b/t/app/controller/reports.t @@ -8,17 +8,85 @@ use DateTime; ok( my $mech = FixMyStreet::TestMech->new, 'Created mech object' ); -$mech->delete_problems_for_council( 2504 ); -$mech->delete_problems_for_council( 2651 ); +$mech->create_body_ok(2514, 'Birmingham City Council'); +my $body_edin_id = $mech->create_body_ok(2651, 'City of Edinburgh Council')->id; +my $body_west_id = $mech->create_body_ok(2504, 'Westminster City Council')->id; +my $body_fife_id = $mech->create_body_ok(2649, 'Fife Council')->id; +my $body_slash_id = $mech->create_body_ok(10000, 'Electricity/Gas Council')->id; -my @edinburgh_problems = $mech->create_problems_for_council(3, 2651, 'All reports'); -my @westminster_problems = $mech->create_problems_for_council(5, 2504, 'All reports'); +$mech->delete_problems_for_body( $body_west_id ); +$mech->delete_problems_for_body( $body_edin_id ); +$mech->delete_problems_for_body( $body_fife_id ); + +my @edinburgh_problems = $mech->create_problems_for_body(3, $body_edin_id, 'All reports'); +my @westminster_problems = $mech->create_problems_for_body(5, $body_west_id, 'All reports'); +my @fife_problems = $mech->create_problems_for_body(15, $body_fife_id, 'All reports'); is scalar @westminster_problems, 5, 'correct number of westminster problems created'; is scalar @edinburgh_problems, 3, 'correct number of edinburgh problems created'; +is scalar @fife_problems, 15, 'correct number of fife problems created'; + +$edinburgh_problems[1]->update( { + state => 'in progress', + confirmed => DateTime->now()->subtract( weeks => 6 ), + lastupdate => DateTime->now()->subtract( weeks => 5 ), +} ); + +$fife_problems[1]->update( { + state => 'fixed - user', + confirmed => DateTime->now()->subtract( weeks => 6 ), + lastupdate => DateTime->now()->subtract( weeks => 5 ), +}); + +$fife_problems[2]->update( { + state => 'fixed - user', + confirmed => DateTime->now()->subtract( weeks => 2 ), + lastupdate => DateTime->now()->subtract( weeks => 1 ), +}); + +$fife_problems[3]->update( { + state => 'fixed - user', + confirmed => DateTime->now()->subtract( weeks => 10 ), + lastupdate => DateTime->now()->subtract( weeks => 9 ), +}); + +$fife_problems[4]->update( { + confirmed => DateTime->now()->subtract( weeks => 10 ), + lastupdate => DateTime->now()->subtract( weeks => 9 ), +}); + +$fife_problems[5]->update( { + confirmed => DateTime->now()->subtract( weeks => 7 ), + lastupdate => DateTime->now()->subtract( weeks => 5 ), +}); + +$fife_problems[6]->update( { + confirmed => DateTime->now()->subtract( weeks => 7 ), + lastupdate => DateTime->now()->subtract( weeks => 2 ), +}); + +$fife_problems[7]->update( { + confirmed => DateTime->now()->subtract( weeks => 10 ), + lastupdate => DateTime->now()->subtract( weeks => 6 ), +}); + +$fife_problems[8]->update( { + confirmed => DateTime->now()->subtract( weeks => 10 ), + lastupdate => DateTime->now()->subtract( weeks => 2 ), +}); + +$fife_problems[9]->update( { + state => 'fixed - user', + confirmed => DateTime->now()->subtract( weeks => 10 ), + lastupdate => DateTime->now()->subtract( weeks => 7 ), +}); + +$fife_problems[10]->update( { + state => 'hidden', +}); # Run the cron script that makes the data for /reports so we don't get an error. -system( "bin/cron-wrapper update-all-reports" ); +system( "bin/update-all-reports" ); # check that we can get the page $mech->get_ok('/reports'); @@ -27,49 +95,192 @@ $mech->content_contains('Birmingham'); my $stats = $mech->extract_report_stats; -is $stats->{'City of Edinburgh Council'}->[1], 3, 'correct number of reports for Edinburgh'; +is $stats->{'City of Edinburgh Council'}->[1], 2, 'correct number of new reports for Edinburgh'; +is $stats->{'City of Edinburgh Council'}->[2], 1, 'correct number of older reports for Edinburgh'; + is $stats->{'Westminster City Council'}->[1], 5, 'correct number of reports for Westminster'; -$mech->follow_link_ok( { text_regex => qr/Birmingham/ } ); +is $stats->{'Fife Council'}->[1], 5, 'correct number of new reports for Fife'; +is $stats->{'Fife Council'}->[2], 4, 'correct number of old reports for Fife'; +is $stats->{'Fife Council'}->[3], 1, 'correct number of unknown reports for Fife'; +is $stats->{'Fife Council'}->[4], 3, 'correct number of fixed reports for Fife'; +is $stats->{'Fife Council'}->[5], 1, 'correct number of older fixed reports for Fife'; + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', +}, sub { + $mech->follow_link_ok( { text_regex => qr/Birmingham/ } ); + $mech->get_ok('/reports/Westminster'); +}; -$mech->get_ok('/reports/Westminster'); $mech->title_like(qr/Westminster City Council/); $mech->content_contains('Westminster City Council'); -$mech->content_contains('All reports Test 3 for 2504', 'problem to be marked non public visible'); +$mech->content_contains('All reports Test 3 for ' . $body_west_id, 'problem to be marked non public visible'); my $problems = $mech->extract_problem_list; is scalar @$problems, 5, 'correct number of problems displayed'; +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', +}, sub { + $mech->get_ok('/reports'); + $mech->follow_link_ok({ url_regex => qr{/reports/Electricity_Gas\+Council} }); + is $mech->uri->path, '/reports/Electricity_Gas+Council', 'Path is correct'; + + $mech->get_ok('/reports/City+of+Edinburgh?t=new'); +}; +$problems = $mech->extract_problem_list; +is scalar @$problems, 2, 'correct number of new problems displayed'; + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', +}, sub { + $mech->get_ok('/reports/City+of+Edinburgh?t=older'); +}; +$problems = $mech->extract_problem_list; +is scalar @$problems, 1, 'correct number of older problems displayed'; + +for my $test ( + { + desc => 'new fife problems on report page', + type => 'new', + expected => 5 + }, + { + desc => 'older fife problems on report page', + type => 'older', + expected => 4 + }, + { + desc => 'unknown fife problems on report page', + type => 'unknown', + expected => 1 + }, + { + desc => 'fixed fife problems on report page', + type => 'fixed', + expected => 3 + }, + { + desc => 'older_fixed fife problems on report page', + type => 'older_fixed', + expected => 1 + }, +) { + subtest $test->{desc} => sub { + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/reports/Fife+Council?t=' . $test->{type}); + }; + + $problems = $mech->extract_problem_list; + is scalar @$problems, $test->{expected}, 'correct number of ' . $test->{type} . ' problems displayed'; + }; +} + my $private = $westminster_problems[2]; ok $private->update( { non_public => 1 } ), 'problem marked non public'; -$mech->get_ok('/reports/Westminster'); +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', +}, sub { + $mech->get_ok('/reports/Westminster'); +}; $problems = $mech->extract_problem_list; is scalar @$problems, 4, 'only public problems are displayed'; -$mech->content_lacks('All reports Test 3 for 2504', 'non public problem is not visible'); +$mech->content_lacks('All reports Test 3 for ' . $body_west_id, 'non public problem is not visible'); $mech->get_ok('/reports'); $stats = $mech->extract_report_stats; is $stats->{'Westminster City Council'}->[1], 5, 'non public reports included in stats'; -SKIP: { - skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 8 ) - unless FixMyStreet::Cobrand->exists('emptyhomes'); - ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes'; - $mech->get_ok('/reports'); - # EHA lacks one column the others have - $mech->content_lacks('state unknown'); +subtest "test fiksgatami all reports page" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fiksgatami' ], + MAPIT_URL => 'http://mapit.nuug.no/', + }, sub { + $mech->create_body_ok(3, 'Oslo'); + ok $mech->host("fiksgatami.no"), 'change host to fiksgatami'; + $mech->get_ok('/reports'); + # There should only be one Oslo + $mech->content_contains('Oslo'); + $mech->content_unlike(qr{Oslo">Oslo.*Oslo}s); + } +}; - skip( "Need 'fiksgatami' in ALLOWED_COBRANDS config", 8 ) - unless FixMyStreet::Cobrand->exists('fiksgatami'); - mySociety::MaPit::configure('http://mapit.nuug.no/'); - ok $mech->host("fiksgatami.no"), 'change host to fiksgatami'; - $mech->get_ok('/reports'); - # There should only be one Oslo - $mech->content_contains('Oslo'); - $mech->content_unlike(qr{Oslo">Oslo.*Oslo}s); -} +subtest "test greenwich all reports page" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'greenwich' ], + MAPIT_URL => 'http://mapit.uk/' + }, sub { + my $body = $mech->create_body_ok(2493, 'Royal Borough of Greenwich'); + my $deleted_contact = $mech->create_contact_ok( + body_id => $body->id, + category => 'Deleted', + email => 'deleted@example.com', + deleted => 1 + ); + ok $mech->host("greenwich.fixmystreet.com"), 'change host to greenwich'; + $mech->get_ok('/reports/Royal+Borough+of+Greenwich'); + # There should not be deleted categories in the list + my $category_select = $mech->forms()->[0]->find_input('filter_category'); + is $category_select, undef, 'deleted categories are not shown'; -done_testing(); + # Clean up after the test + $deleted_contact->delete; + } +}; + +subtest "it lists shortlisted reports" => sub { + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/' + }, sub { + my $body = FixMyStreet::App->model('DB::Body')->find( $body_edin_id ); + my $user = $mech->log_in_ok( 'test@example.com' ); + $user->update({ from_body => $body }); + $user->user_body_permissions->find_or_create({ + body => $body, + permission_type => 'planned_reports', + }); + + my ($shortlisted_problem) = $mech->create_problems_for_body(1, $body_edin_id, 'Shortlisted report'); + my ($unshortlisted_problem) = $mech->create_problems_for_body(1, $body_edin_id, 'Unshortlisted report'); + my ($removed_from_shortlist_problem) = $mech->create_problems_for_body(1, $body_edin_id, 'Removed from shortlist report'); + + $user->add_to_planned_reports($shortlisted_problem); + $user->add_to_planned_reports($removed_from_shortlist_problem); + $user->remove_from_planned_reports($removed_from_shortlist_problem); + $mech->get_ok('/reports/City+of+Edinburgh+Council'); + $mech->content_contains('<option value="shortlisted">Shortlisted</option>'); + $mech->content_contains('<option value="unshortlisted">Unshortlisted</option>'); + + $mech->get_ok('/reports/City+of+Edinburgh+Council?status=shortlisted'); + + $mech->content_contains('Shortlisted report'); + $mech->content_lacks('Unshortlisted report'); + $mech->content_lacks('Removed from shortlist report'); + + $mech->get_ok('/reports/City+of+Edinburgh+Council?status=shortlisted,open'); + + $mech->content_contains('Shortlisted report'); + $mech->content_lacks('Unshortlisted report'); + $mech->content_lacks('Removed from shortlist report'); + + $mech->get_ok('/reports/City+of+Edinburgh+Council?status=unshortlisted,open'); + + $mech->content_contains('Unshortlisted report'); + $mech->content_contains('Removed from shortlist report'); + $mech->content_lacks('Shortlisted report'); + + $user->admin_user_body_permissions->delete; + + $mech->get_ok('/reports/City+of+Edinburgh+Council'); + $mech->content_lacks('<option value="shortlisted">Shortlisted</option>'); + $mech->content_lacks('<option value="unshortlisted">Unshortlisted</option>'); + }; +}; + +done_testing(); diff --git a/t/app/controller/rss.t b/t/app/controller/rss.t index 77e2c7ee1..bec504760 100644 --- a/t/app/controller/rss.t +++ b/t/app/controller/rss.t @@ -3,6 +3,7 @@ use warnings; use Test::More; use FixMyStreet::TestMech; +use FixMyStreet::App; my $mech = FixMyStreet::TestMech->new; @@ -12,12 +13,13 @@ my $dt = DateTime->new( day => 10 ); -my $user1 = FixMyStreet::App->model('DB::User') - ->find_or_create( { email => 'reporter@example.com', name => 'Reporter User' } ); +my $user1 = $mech->create_user_ok('reporter-rss@example.com', name => 'Reporter User'); + +my $dt_parser = FixMyStreet::App->model('DB')->schema->storage->datetime_parser; my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { postcode => 'eh1 1BB', - council => '2651', + bodies_str => '2651', areas => ',11808,135007,14419,134935,2651,20728,', category => 'Street lighting', title => 'Testing', @@ -26,9 +28,9 @@ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { name => $user1->name, anonymous => 0, state => 'confirmed', - confirmed => $dt, - lastupdate => $dt, - whensent => $dt->clone->add( minutes => 5 ), + confirmed => $dt_parser->format_datetime($dt), + lastupdate => $dt_parser->format_datetime($dt), + whensent => $dt_parser->format_datetime($dt->clone->add( minutes => 5 )), lang => 'en-gb', service => '', cobrand => 'default', @@ -39,10 +41,16 @@ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { user_id => $user1->id, } ); - -$mech->get_ok("/rss/pc/EH11BB/2"); +$mech->host('www.fixmystreet.com'); +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + MAPIT_URL => 'http://mapit.uk/', +}, sub { + $mech->get_ok("/rss/pc/EH11BB/2"); +}; $mech->content_contains( "Testing, 10th October" ); $mech->content_lacks( 'Nearest road to the pin' ); +is $mech->response->header('Access-Control-Allow-Origin'), '*'; $report->geocode( { @@ -108,11 +116,87 @@ $report->geocode( ); $report->update(); -$mech->get_ok("/rss/pc/EH11BB/2"); +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'fixmystreet' ], + MAPIT_URL => 'http://mapit.uk/', +}, sub { + $mech->get_ok("/rss/pc/EH11BB/2"); +}; $mech->content_contains( "Testing, 10th October" ); $mech->content_contains( '18 North Bridge, Edinburgh' ); $report->delete(); -$user1->delete(); + +my $council = $mech->create_body_ok(2333, 'Hart Council'); +my $county = $mech->create_body_ok(2227, 'Hampshire Council'); + +my $now = DateTime->now(); +my $report_to_council = FixMyStreet::App->model('DB::Problem')->find_or_create( + { + postcode => 'GU51 4AE', + bodies_str => $council->id, + areas => ',2333,2227,', + category => 'Other', + title => 'council report', + detail => 'Test 2 Detail', + used_map => 't', + name => 'Test User', + anonymous => 'f', + state => 'closed', + confirmed => $now->ymd . ' ' . $now->hms, + lang => 'en-gb', + service => '', + cobrand => 'default', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.279616', + longitude => '-0.846040', + user_id => $user1->id, + } +); + +my $report_to_county_council = FixMyStreet::App->model('DB::Problem')->find_or_create( + { + postcode => 'GU51 4AE', + bodies_str => $county->id, + areas => ',2333,2227,', + category => 'Other', + title => 'county report', + detail => 'Test 2 Detail', + used_map => 't', + name => 'Test User', + anonymous => 'f', + state => 'closed', + confirmed => $now->ymd . ' ' . $now->hms, + lang => 'en-gb', + service => '', + cobrand => 'default', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.279616', + longitude => '-0.846040', + user_id => $user1->id, + } +); + +subtest "check RSS feeds on cobrand have correct URLs for non-cobrand reports" => sub { + $mech->host('hart.fixmystreet.com'); + my $expected1 = FixMyStreet->config('BASE_URL') . '/report/' . $report_to_county_council->id; + my $expected2; + + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'hart' ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok("/rss/area/Hart"); + my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker('hart')->new(); + $expected2 = $cobrand->base_url . '/report/' . $report_to_council->id; + }; + + $mech->content_contains($expected1, 'non cobrand area report point to fixmystreet.com'); + $mech->content_contains($expected2, 'cobrand area report point to cobrand url'); +}; + +$mech->delete_user( $user1 ); done_testing(); diff --git a/t/app/controller/sample.jpg b/t/app/controller/sample.jpg Binary files differindex 23198cb83..b84c0c94e 100644 --- a/t/app/controller/sample.jpg +++ b/t/app/controller/sample.jpg diff --git a/t/app/controller/token.t b/t/app/controller/token.t new file mode 100644 index 000000000..ac88f4f7a --- /dev/null +++ b/t/app/controller/token.t @@ -0,0 +1,35 @@ +use strict; +use warnings; +use Test::More; +use utf8; + +use FixMyStreet::TestMech; +use FixMyStreet::App; + +my $mech = FixMyStreet::TestMech->new; +my $user = $mech->create_user_ok('bob@example.com', name => 'Bob'); + +subtest 'Zurich special case for C::Tokens->problem_confirm' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => ['zurich'], + }, sub { + my $c = FixMyStreet::App->new; + my $zurich = $mech->create_body_ok( 1, 'Zurich' ); + my ($report) = $mech->create_problems_for_body( + 1, $zurich->id, + { + state => 'unconfirmed', + confirmed => undef, + cobrand => 'zurich', + }); + + is $report->get_extra_metadata('email_confirmed'), undef, 'email_confirmed not yet set (sanity)'; + my $token = $c->model('DB::Token')->create({ scope => 'problem', data => $report->id }); + + $mech->get_ok('/P/' . $token->token); + $report->discard_changes; + is $report->get_extra_metadata('email_confirmed'), 1, 'email_confirmed set by Zurich special case'; + }; +}; + +done_testing; |