diff options
Diffstat (limited to 't/app')
24 files changed, 2756 insertions, 947 deletions
diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t index bd0f9e408..398ce8ea6 100644 --- a/t/app/controller/admin.t +++ b/t/app/controller/admin.t @@ -1,4 +1,6 @@ use FixMyStreet::TestMech; +# avoid wide character warnings from the category change message +use open ':std', ':encoding(UTF-8)'; my $mech = FixMyStreet::TestMech->new; @@ -287,6 +289,7 @@ subtest 'check text output' => sub { $mech->get_ok('/admin/body/' . $body->id . '?text=1'); is $mech->content_type, 'text/plain'; $mech->content_contains('test category'); + $mech->content_lacks('<body'); }; @@ -316,7 +319,7 @@ foreach my $test ( detail => 'Detail for Report to Edit', state => 'confirmed', name => 'Test User', - email => $user->email, + username => $user->email, anonymous => 0, flagged => undef, non_public => undef, @@ -332,7 +335,7 @@ foreach my $test ( detail => 'Detail for Report to Edit', state => 'confirmed', name => 'Test User', - email => $user->email, + username => $user->email, anonymous => 0, flagged => undef, non_public => undef, @@ -348,7 +351,7 @@ foreach my $test ( detail => 'Edited Detail', state => 'confirmed', name => 'Test User', - email => $user->email, + username => $user->email, anonymous => 0, flagged => undef, non_public => undef, @@ -365,7 +368,7 @@ foreach my $test ( detail => 'Edited Detail', state => 'confirmed', name => 'Edited User', - email => $user->email, + username => $user->email, anonymous => 0, flagged => undef, non_public => undef, @@ -384,12 +387,12 @@ foreach my $test ( detail => 'Edited Detail', state => 'confirmed', name => 'Edited User', - email => $user->email, + username => $user->email, anonymous => 0, flagged => 'on', non_public => undef, }, - changes => { email => $user2->email, }, + changes => { username => $user2->email, }, log_entries => [qw/edit edit edit edit edit/], resend => 0, user => $user2, @@ -401,11 +404,12 @@ foreach my $test ( detail => 'Edited Detail', state => 'confirmed', name => 'Edited User', - email => $user2->email, + username => $user2->email, anonymous => 0, flagged => 'on', non_public => undef, }, + expect_comment => 1, changes => { state => 'unconfirmed' }, log_entries => [qw/edit state_change edit edit edit edit edit/], resend => 0, @@ -417,11 +421,12 @@ foreach my $test ( detail => 'Edited Detail', state => 'unconfirmed', name => 'Edited User', - email => $user2->email, + username => $user2->email, anonymous => 0, flagged => 'on', non_public => undef, }, + expect_comment => 1, changes => { state => 'confirmed' }, log_entries => [qw/edit state_change edit state_change edit edit edit edit edit/], resend => 0, @@ -433,11 +438,12 @@ foreach my $test ( detail => 'Edited Detail', state => 'confirmed', name => 'Edited User', - email => $user2->email, + username => $user2->email, anonymous => 0, flagged => 'on', non_public => undef, }, + expect_comment => 1, changes => { state => 'fixed' }, log_entries => [qw/edit state_change edit state_change edit state_change edit edit edit edit edit/], @@ -450,11 +456,12 @@ foreach my $test ( detail => 'Edited Detail', state => 'fixed', name => 'Edited User', - email => $user2->email, + username => $user2->email, anonymous => 0, flagged => 'on', non_public => undef, }, + expect_comment => 1, changes => { state => 'hidden' }, log_entries => [ qw/edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ @@ -468,11 +475,12 @@ foreach my $test ( detail => 'Edited Detail', state => 'hidden', name => 'Edited User', - email => $user2->email, + username => $user2->email, anonymous => 0, flagged => 'on', non_public => undef, }, + expect_comment => 1, changes => { state => 'confirmed', anonymous => 1, @@ -489,7 +497,7 @@ foreach my $test ( detail => 'Edited Detail', state => 'confirmed', name => 'Edited User', - email => $user2->email, + username => $user2->email, anonymous => 1, flagged => 'on', non_public => undef, @@ -507,7 +515,7 @@ foreach my $test ( detail => 'Edited Detail', state => 'confirmed', name => 'Edited User', - email => $user2->email, + username => $user2->email, anonymous => 1, flagged => 'on', non_public => undef, @@ -520,10 +528,58 @@ foreach my $test ( ], resend => 0, }, + { + description => 'change state to investigating as body superuser', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user2->email, + anonymous => 1, + flagged => 'on', + non_public => 'on', + }, + expect_comment => 1, + user_body => $oxfordshire, + changes => { state => 'investigating' }, + log_entries => [ + qw/edit state_change edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 0, + }, + { + description => 'change state to in progess and change category as body superuser', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'investigating', + name => 'Edited User', + username => $user2->email, + anonymous => 1, + flagged => 'on', + non_public => 'on', + }, + expect_comment => 1, + expected_text => '*Category changed from ‘Other’ to ‘Potholes’*', + user_body => $oxfordshire, + changes => { state => 'in progress', category => 'Potholes' }, + log_entries => [ + qw/edit state_change edit state_change edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 0, + }, ) { subtest $test->{description} => sub { + $report->comments->delete; $log_entries->reset; + + if ( $test->{user_body} ) { + $superuser->from_body( $test->{user_body}->id ); + $superuser->update; + } + $mech->get_ok("/admin/report_edit/$report_id"); @{$test->{fields}}{'external_id', 'external_body', 'external_team', 'category'} = (13, "", "", "Other"); @@ -555,7 +611,7 @@ foreach my $test ( $test->{changes}->{flagged} = 1 if $test->{changes}->{flagged}; $test->{changes}->{non_public} = 1 if $test->{changes}->{non_public}; - is $report->$_, $test->{changes}->{$_}, "$_ updated" for grep { $_ ne 'email' } keys %{ $test->{changes} }; + is $report->$_, $test->{changes}->{$_}, "$_ updated" for grep { $_ ne 'username' } keys %{ $test->{changes} }; if ( $test->{user} ) { is $report->user->id, $test->{user}->id, 'user changed'; @@ -565,6 +621,31 @@ foreach my $test ( $mech->content_contains( 'That problem will now be resent' ); is $report->whensent, undef, 'mark report to resend'; } + + if ( $test->{expect_comment} ) { + my $comment = $report->comments->first; + ok $comment, 'report status change creates comment'; + is $report->comments->count, 1, 'report only has one comment'; + if ($test->{expected_text}) { + is $comment->text, $test->{expected_text}, 'comment has expected text'; + } else { + is $comment->text, '', 'comment has no text'; + } + if ( $test->{user_body} ) { + ok $comment->get_extra_metadata('is_body_user'), 'body user metadata set'; + ok !$comment->get_extra_metadata('is_superuser'), 'superuser metadata not set'; + is $comment->name, $test->{user_body}->name, 'comment name is body name'; + } else { + ok !$comment->get_extra_metadata('is_body_user'), 'body user metadata not set'; + ok $comment->get_extra_metadata('is_superuser'), 'superuser metadata set'; + is $comment->name, _('an administrator'), 'comment name is admin'; + } + } else { + is $report->comments->count, 0, 'report has no comments'; + } + + $superuser->from_body(undef); + $superuser->update; }; } @@ -586,6 +667,8 @@ subtest 'change report category' => sub { $ox_report->discard_changes; is $ox_report->category, 'Traffic lights'; isnt $ox_report->whensent, undef; + is $ox_report->comments->count, 1, "Comment created for update"; + is $ox_report->comments->first->text, '*Category changed from ‘Potholes’ to ‘Traffic lights’*', 'Comment text correct'; $mech->submit_form_ok( { with_fields => { category => 'Graffiti' } }, 'form_submitted' ); $ox_report->discard_changes; @@ -603,8 +686,8 @@ subtest 'change email to new user' => sub { detail => $report->detail, state => $report->state, name => $report->name, - email => $report->user->email, - category => 'Other', + username => $report->user->email, + category => 'Potholes', anonymous => 1, flagged => 'on', non_public => 'on', @@ -616,12 +699,10 @@ subtest 'change email to new user' => sub { is_deeply( $mech->visible_form_values(), $fields, 'initial form values' ); my $changes = { - email => 'test3@example.com' + username => 'test3@example.com' }; - $user3 = - FixMyStreet::App->model('DB::User') - ->find( { email => 'test3@example.com', name => 'Test User 2' } ); + $user3 = FixMyStreet::App->model('DB::User')->find( { email => 'test3@example.com' } ); ok !$user3, 'user not in database'; @@ -640,9 +721,7 @@ subtest 'change email to new user' => sub { is $log_entries->first->action, 'edit', 'log action'; is_deeply( $mech->visible_form_values(), $new_fields, 'changed form values' ); - $user3 = - FixMyStreet::App->model('DB::User') - ->find( { email => 'test3@example.com', name => 'Test User 2' } ); + $user3 = FixMyStreet::App->model('DB::User')->find( { email => 'test3@example.com' } ); $report->discard_changes; @@ -657,18 +736,50 @@ subtest 'adding email to abuse list from report page' => sub { $abuse->delete if $abuse; $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_contains('Ban email address'); + $mech->content_contains('Ban user'); $mech->click_ok('banuser'); - $mech->content_contains('Email added to abuse list'); - $mech->content_contains('<small>(Email in abuse table)</small>'); + $mech->content_contains('User added to abuse list'); + $mech->content_contains('<small>User in abuse table</small>'); $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); ok $abuse, 'entry created in abuse table'; $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_contains('<small>(Email in abuse table)</small>'); + $mech->content_contains('<small>User in abuse table</small>'); +}; + +subtest 'remove user from abuse list from edit user page' => sub { + my $abuse = FixMyStreet::App->model('DB::Abuse')->find_or_create( { email => $user->email } ); + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->content_contains('User in abuse table'); + + $mech->click_ok('unban'); + + $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $user->email } ); + ok !$abuse, 'record removed from abuse table'; +}; + +subtest 'remove user with phone account from abuse list from edit user page' => sub { + my $abuse_user = $mech->create_user_ok('01234 456789'); + my $abuse = FixMyStreet::App->model('DB::Abuse')->find_or_create( { email => $abuse_user->phone } ); + $mech->get_ok( '/admin/user_edit/' . $abuse_user->id ); + $mech->content_contains('User in abuse table'); + my $abuse_found = FixMyStreet::App->model('DB::Abuse')->find( { email => $abuse_user->phone } ); + ok $abuse_found, 'user in abuse table'; + + $mech->click_ok('unban'); + + $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $user->phone } ); + ok !$abuse, 'record removed from abuse table'; +}; + +subtest 'no option to remove user already in abuse list' => sub { + my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $user->email } ); + $abuse->delete if $abuse; + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->content_lacks('User in abuse table'); }; subtest 'flagging user from report page' => sub { @@ -742,7 +853,7 @@ for my $test ( state => 'confirmed', name => '', anonymous => 1, - email => 'test@example.com', + username => 'test@example.com', }, changes => { text => 'this is a changed update', @@ -757,7 +868,7 @@ for my $test ( state => 'confirmed', name => '', anonymous => 1, - email => 'test@example.com', + username => 'test@example.com', }, changes => { name => 'A User', @@ -772,7 +883,7 @@ for my $test ( state => 'confirmed', name => 'A User', anonymous => 1, - email => 'test@example.com', + username => 'test@example.com', }, changes => { anonymous => 0, @@ -787,11 +898,10 @@ for my $test ( state => 'confirmed', name => 'A User', anonymous => 0, - email => $update->user->email, - email => 'test@example.com', + username => 'test@example.com', }, changes => { - email => 'test2@example.com', + username => 'test2@example.com', }, log_count => 4, log_entries => [qw/edit edit edit edit/], @@ -804,7 +914,7 @@ for my $test ( state => 'confirmed', name => 'A User', anonymous => 0, - email => 'test2@example.com', + username => 'test2@example.com', }, changes => { state => 'unconfirmed', @@ -819,7 +929,7 @@ for my $test ( state => 'unconfirmed', name => 'A User', anonymous => 0, - email => 'test2@example.com', + username => 'test2@example.com', }, changes => { text => 'this is a twice changed update', @@ -849,7 +959,7 @@ for my $test ( $update->discard_changes; - is $update->$_, $test->{changes}->{$_} for grep { $_ ne 'email' } keys %{ $test->{changes} }; + is $update->$_, $test->{changes}->{$_} for grep { $_ ne 'username' } keys %{ $test->{changes} }; if ( $test->{changes}{state} && $test->{changes}{state} eq 'confirmed' ) { isnt $update->confirmed, undef; } @@ -935,9 +1045,7 @@ for my $test ( } subtest 'editing update email creates new user if required' => sub { - my $user = FixMyStreet::App->model('DB::User')->find( - { email => 'test4@example.com' } - ); + my $user = FixMyStreet::App->model('DB::User')->find( { email => 'test4@example.com' } ); $user->delete if $user; @@ -946,14 +1054,12 @@ subtest 'editing update email creates new user if required' => sub { state => 'hidden', name => 'A User', anonymous => 0, - email => 'test4@example.com', + username => 'test4@example.com', }; $mech->submit_form_ok( { with_fields => $fields } ); - $user = FixMyStreet::App->model('DB::User')->find( - { email => 'test4@example.com' } - ); + $user = FixMyStreet::App->model('DB::User')->find( { email => 'test4@example.com' } ); is_deeply $mech->visible_form_values, $fields, 'submitted form values'; @@ -970,18 +1076,18 @@ subtest 'adding email to abuse list from update page' => sub { $abuse->delete if $abuse; $mech->get_ok( '/admin/update_edit/' . $update->id ); - $mech->content_contains('Ban email address'); + $mech->content_contains('Ban user'); $mech->click_ok('banuser'); - $mech->content_contains('Email added to abuse list'); - $mech->content_contains('<small>(Email in abuse table)</small>'); + $mech->content_contains('User added to abuse list'); + $mech->content_contains('<small>User in abuse table</small>'); $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); ok $abuse, 'entry created in abuse table'; $mech->get_ok( '/admin/update_edit/' . $update->id ); - $mech->content_contains('<small>(Email in abuse table)</small>'); + $mech->content_contains('<small>User in abuse table</small>'); }; subtest 'flagging user from update page' => sub { @@ -1029,13 +1135,12 @@ subtest 'hiding comment marked as fixed reopens report' => sub { $report->state('fixed - user'); $report->update; - my $fields = { text => 'this is a changed update', state => 'hidden', name => 'A User', anonymous => 0, - email => 'test2@example.com', + username => 'test2@example.com', }; $mech->submit_form_ok( { with_fields => $fields } ); @@ -1092,7 +1197,7 @@ subtest 'report search' => sub { subtest 'search abuse' => sub { $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); + $mech->content_like(qr{test4\@example.com.*</td>\s*<td>.*?</td>\s*<td>User in abuse table}s); }; subtest 'show flagged entries' => sub { @@ -1168,6 +1273,69 @@ $user->update; my $southend = $mech->create_body_ok(2607, 'Southend-on-Sea Borough Council'); +for my $test ( + { + desc => 'add user - blank form', + fields => { + email => '', email_verified => 0, + phone => '', phone_verified => 0, + }, + error => ['Please verify at least one of email/phone', 'Please enter a name'], + }, + { + desc => 'add user - blank, verify phone', + fields => { + email => '', email_verified => 0, + phone => '', phone_verified => 1, + }, + error => ['Please enter a valid email or phone number', 'Please enter a name'], + }, + { + desc => 'add user - bad email', + fields => { + name => 'Norman', + email => 'bademail', email_verified => 0, + phone => '', phone_verified => 0, + }, + error => ['Please enter a valid email'], + }, + { + desc => 'add user - bad phone', + fields => { + name => 'Norman', + phone => '01214960000000', phone_verified => 1, + }, + error => ['Please check your phone number is correct'], + }, + { + desc => 'add user - landline', + fields => { + name => 'Norman Name', + phone => '+441214960000', + phone_verified => 1, + }, + error => ['Please enter a mobile number'], + }, + { + desc => 'add user - good details', + fields => { + name => 'Norman Name', + phone => '+61491570156', + phone_verified => 1, + }, + }, +) { + subtest $test->{desc} => sub { + $mech->get_ok('/admin/users'); + $mech->submit_form_ok( { with_fields => $test->{fields} } ); + if ($test->{error}) { + $mech->content_contains($_) for @{$test->{error}}; + } else { + $mech->content_contains('Updated'); + } + }; +} + my %default_perms = ( "permissions[moderate]" => undef, "permissions[planned_reports]" => undef, @@ -1190,6 +1358,10 @@ my %default_perms = ( trusted_bodies => undef, ); +# Start this section with user having no name +# Regression test for mysociety/fixmystreetforcouncils#250 +$user->update({ name => '' }); + FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', }, sub { @@ -1197,7 +1369,7 @@ FixMyStreet::override_config { { desc => 'edit user name', fields => { - name => 'Test User', + name => '', email => 'test@example.com', body => $haringey->id, phone => '', @@ -1358,6 +1530,32 @@ FixMyStreet::override_config { } }; +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + SMS_AUTHENTICATION => 1, +}, sub { + subtest "Test edit user add verified phone" => sub { + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok( { with_fields => { + phone => '+61491570157', + phone_verified => 1, + } } ); + $mech->content_contains( 'Updated!' ); + }; + + subtest "Test changing user to an existing one" => sub { + my $existing_user = $mech->create_user_ok('existing@example.com', name => 'Existing User'); + $mech->create_problems_for_body(2, 2514, 'Title', { user => $existing_user }); + my $count = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id })->count; + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok( { with_fields => { email => 'existing@example.com' } }, 'submit email change' ); + is $mech->uri->path, '/admin/user_edit/' . $existing_user->id, 'redirected'; + my $p = FixMyStreet::DB->resultset('Problem')->search({ user_id => $existing_user->id })->count; + is $p, $count + 2, 'reports merged'; + }; + +}; + 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"); @@ -1380,6 +1578,7 @@ subtest "Check admin_base_url" => sub { $mech->log_out_ok; subtest "Users without from_body can't access admin" => sub { + $user = FixMyStreet::App->model('DB::User')->find( { email => 'existing@example.com' } ); $user->from_body( undef ); $user->update; @@ -1460,6 +1659,108 @@ subtest "response templates are included on page" => sub { }; }; +subtest "auto-response templates that duplicate a single category can't be added" => sub { + $mech->delete_response_template($_) for $oxfordshire->response_templates; + my $template = $oxfordshire->response_templates->create({ + title => "Report fixed - potholes", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 1, + state => 'fixed - council', + }); + $template->contact_response_templates->find_or_create({ + contact_id => $oxfordshirecontact->id, + }); + is $oxfordshire->response_templates->count, 1, "Initial response template was created"; + + + $mech->log_in_ok( $superuser->email ); + $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); + + # This response template has the same category & state as an existing one + # so won't be allowed. + my $fields = { + title => "Report marked fixed - potholes", + text => "Thank you for your report. This pothole has been fixed.", + auto_response => 'on', + state => 'fixed - council', + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + is $mech->uri->path, '/admin/templates/' . $oxfordshire->id . '/new', 'not redirected'; + $mech->content_contains( 'Please correct the errors below' ); + $mech->content_contains( 'There is already an auto-response template for this category/state.' ); + + is $oxfordshire->response_templates->count, 1, "Duplicate response template wasn't added"; +}; + +subtest "auto-response templates that duplicate all categories can't be added" => sub { + $mech->delete_response_template($_) for $oxfordshire->response_templates; + $oxfordshire->response_templates->create({ + title => "Report investigating - all cats", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 1, + state => 'fixed - council', + }); + is $oxfordshire->response_templates->count, 1, "Initial response template was created"; + + + $mech->log_in_ok( $superuser->email ); + $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); + + # There's already a response template for all categories and this state, so + # this new template won't be allowed. + my $fields = { + title => "Report investigating - single cat", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 'on', + state => 'fixed - council', + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + is $mech->uri->path, '/admin/templates/' . $oxfordshire->id . '/new', 'not redirected'; + $mech->content_contains( 'Please correct the errors below' ); + $mech->content_contains( 'There is already an auto-response template for this category/state.' ); + + + is $oxfordshire->response_templates->count, 1, "Duplicate response template wasn't added"; +}; + +subtest "all-category auto-response templates that duplicate a single category can't be added" => sub { + $mech->delete_response_template($_) for $oxfordshire->response_templates; + my $template = $oxfordshire->response_templates->create({ + title => "Report fixed - potholes", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 1, + state => 'fixed - council', + }); + $template->contact_response_templates->find_or_create({ + contact_id => $oxfordshirecontact->id, + }); + is $oxfordshire->response_templates->count, 1, "Initial response template was created"; + + + $mech->log_in_ok( $superuser->email ); + $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); + + # This response template is implicitly for all categories, but there's + # already a template for a specific category in this state, so it won't be + # allowed. + my $fields = { + title => "Report marked fixed - all cats", + text => "Thank you for your report. This problem has been fixed.", + auto_response => 'on', + state => 'fixed - council', + }; + $mech->submit_form_ok( { with_fields => $fields } ); + is $mech->uri->path, '/admin/templates/' . $oxfordshire->id . '/new', 'not redirected'; + $mech->content_contains( 'Please correct the errors below' ); + $mech->content_contains( 'There is already an auto-response template for this category/state.' ); + + is $oxfordshire->response_templates->count, 1, "Duplicate response template wasn't added"; +}; + + + $mech->log_in_ok( $superuser->email ); subtest "response priorities can be added" => sub { @@ -1475,8 +1776,8 @@ subtest "response priorities can be added" => sub { }; $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"; + is $oxfordshire->response_priorities->count, 1, "Response priority was added to body"; + is $oxfordshirecontact->response_priorities->count, 1, "Response priority was added to contact"; }; subtest "response priorities can set to default" => sub { @@ -1494,7 +1795,7 @@ subtest "response priorities can set to default" => sub { $mech->submit_form_ok( { with_fields => $fields } ); is $oxfordshire->response_priorities->count, 1, "Still one response priority"; - is $oxfordshirecontact->response_priorities->count, 1, "Still one response template"; + is $oxfordshirecontact->response_priorities->count, 1, "Still one response priority"; ok $oxfordshire->response_priorities->first->is_default, "Response priority set to default"; }; @@ -1511,8 +1812,8 @@ subtest "response priorities are limited by body" => sub { 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"; + is $bromley->response_priorities->count, 1, "Response priority was added to Bromley"; + is $oxfordshire->response_priorities->count, 1, "Response priority wasn't added to Oxfordshire"; $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); $mech->content_lacks( $bromleypriority->name ); @@ -1547,4 +1848,10 @@ subtest "response priorities can't be viewed across councils" => sub { }; }; +subtest "smoke view some stats pages" => sub { + $mech->log_in_ok( $superuser->email ); + $mech->get_ok('/admin/stats/fix-rate'); + $mech->get_ok('/admin/stats/questionnaire'); +}; + done_testing(); diff --git a/t/app/controller/alert_new.t b/t/app/controller/alert_new.t index 97a19b3b8..4e8fd1b29 100644 --- a/t/app/controller/alert_new.t +++ b/t/app/controller/alert_new.t @@ -475,7 +475,7 @@ subtest "Test normal alert signups and that alerts are sent" => sub { $mech->delete_user($user2); }; -subtest "Test alerts are correct for no-text updates" => sub { +subtest "Test alerts are not sent for no-text updates" => sub { $mech->delete_user( 'reporter@example.com' ); $mech->delete_user( 'alerts@example.com' ); @@ -513,6 +513,40 @@ subtest "Test alerts are correct for no-text updates" => sub { my $report_id = $report->id; ok $report, "created test report - $report_id"; + my $report2 = FixMyStreet::App->model('DB::Problem')->create( { + postcode => 'EH1 1BB', + bodies_str => '1', + 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 $report2_id = $report2->id; + ok $report2, "created test report - $report2_id"; + + # Must be first + my $alert2 = FixMyStreet::App->model('DB::Alert')->create( { + parameter => $report2_id, + alert_type => 'new_updates', + user => $user2, + } )->confirm; + ok $alert2, 'created alert for other user'; + my $alert = FixMyStreet::App->model('DB::Alert')->create( { parameter => $report_id, alert_type => 'new_updates', @@ -533,6 +567,92 @@ subtest "Test alerts are correct for no-text updates" => sub { my $update_id = $update->id; ok $update, "created test update from staff user - $update_id"; + my $update2 = FixMyStreet::App->model('DB::Comment')->create( { + problem_id => $report2_id, + user_id => $user3->id, + name => 'Staff User', + mark_fixed => 'false', + text => 'This is a normal update', + state => 'confirmed', + confirmed => $dt->clone->add( hours => 9 ), + anonymous => 'f', + } ); + my $update2_id = $update2->id; + ok $update2, "created test update from staff user - $update2_id"; + + $mech->clear_emails_ok; + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + FixMyStreet::Script::Alerts::send(); + }; + + $mech->email_count_is(1); + + $mech->delete_user($user1); + $mech->delete_user($user2); + $mech->delete_user($user3); +}; + +subtest "Test no marked as confirmed added to alerts" => 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 $user3 = $mech->create_user_ok('staff@example.com', name => 'Staff User', from_body => $gloucester ); + 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 => '1', + areas => ',11808,135007,14419,134935,2651,20728,', + category => 'Street lighting', + title => 'Testing', + detail => 'Testing Detail', + used_map => 1, + name => $user1->name, + anonymous => 0, + state => 'confirmed', + 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 => $user2, + } )->confirm; + ok $alert, 'created alert for other user'; + + my $update = FixMyStreet::App->model('DB::Comment')->create( { + problem_id => $report_id, + user_id => $user3->id, + name => 'Staff User', + mark_fixed => 'false', + text => 'this is update', + state => 'confirmed', + problem_state => 'confirmed', + confirmed => $dt->clone->add( hours => 9 ), + anonymous => 'f', + } ); + my $update_id = $update->id; + ok $update, "created test update from staff user - $update_id"; + $mech->clear_emails_ok; FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', @@ -545,15 +665,116 @@ subtest "Test alerts are correct for no-text updates" => sub { my $body = $mech->get_text_body_from_email($email); like $body, qr/The following updates have been left on this report:/, 'email is about updates to existing report'; like $body, qr/Staff User/, 'Update comes from correct user'; - - my @urls = $mech->get_link_from_email($email, 1); - is $urls[0], "http://www.example.org/report/" . $report_id, "Correct report URL in email"; + unlike $body, qr/State changed to: Open/s, 'no marked as confirmed text'; $mech->delete_user($user1); $mech->delete_user($user2); $mech->delete_user($user3); }; +for my $test ( + { + update_text => '', + problem_state => 'investigating', + expected_text => 'State changed to: Investigating', + desc => 'comment changing status included in email', + }, + { + update_text => 'Category changed to Potholes', + problem_state => '', + expected_text => 'Category changed to Potholes', + desc => 'comment about category included', + }, + { + update_text => 'Category changed to Potholes', + problem_state => 'investigating', + expected_text => 'Category changed to Potholes.*Investigating', + desc => 'comment about category and status change included', + }, +) { + subtest $test->{desc} => 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 $user3 = $mech->create_user_ok('staff@example.com', name => 'Staff User', from_body => $gloucester ); + 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 => '1', + areas => ',11808,135007,14419,134935,2651,20728,', + category => 'Street lighting', + title => 'Testing', + detail => 'Testing Detail', + used_map => 1, + name => $user1->name, + anonymous => 0, + state => 'confirmed', + 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 => $user2, + } )->confirm; + ok $alert, 'created alert for other user'; + + my $update = FixMyStreet::App->model('DB::Comment')->create( { + problem_id => $report_id, + user_id => $user3->id, + name => 'Staff User', + mark_fixed => 'false', + text => $test->{update_text}, + problem_state => $test->{problem_state}, + state => 'confirmed', + confirmed => $dt->clone->add( hours => 9 ), + anonymous => 'f', + } ); + my $update_id = $update->id; + ok $update, "created test update from staff user - $update_id"; + + $mech->clear_emails_ok; + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + FixMyStreet::Script::Alerts::send(); + }; + + $mech->email_count_is(1); + my $expected_text = $test->{expected_text}; + my $email = $mech->get_email; + my $body = $mech->get_text_body_from_email($email); + like $body, qr/The following updates have been left on this report:/, 'email is about updates to existing report'; + like $body, qr/Staff User/, 'Update comes from correct user'; + like $body, qr/$expected_text/s, 'Expected text present'; + + my @urls = $mech->get_link_from_email($email, 1); + is $urls[0], "http://www.example.org/report/" . $report_id, "Correct report URL in email"; + + $mech->delete_user($user1); + $mech->delete_user($user2); + $mech->delete_user($user3); + }; +} + subtest "Test signature template is used from cobrand" => sub { $mech->delete_user( 'reporter@example.com' ); $mech->delete_user( 'alerts@example.com' ); diff --git a/t/app/controller/around.t b/t/app/controller/around.t index fbb4e76cd..d1254edb7 100644 --- a/t/app/controller/around.t +++ b/t/app/controller/around.t @@ -1,3 +1,5 @@ +use Test::MockModule; + use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; @@ -92,15 +94,22 @@ foreach my $test ( }; } -subtest 'check non public reports are not displayed on around page' => sub { - my $params = { - postcode => 'EH1 1BB', - latitude => 55.9519637512, - longitude => -3.17492254484, - }; - my @edinburgh_problems = - $mech->create_problems_for_body( 5, 2651, 'Around page', $params ); +my @edinburgh_problems = $mech->create_problems_for_body( 5, 2651, 'Around page', { + postcode => 'EH1 1BB', + latitude => 55.9519637512, + longitude => -3.17492254484, +}); +subtest 'check lookup by reference' => sub { + $mech->get_ok('/'); + $mech->submit_form_ok( { with_fields => { pc => 'ref:12345' } }, 'bad ref'); + $mech->content_contains('Searching found no reports'); + my $id = $edinburgh_problems[0]->id; + $mech->submit_form_ok( { with_fields => { pc => "ref:$id" } }, 'good ref'); + is $mech->uri->path, "/report/$id", "redirected to report page"; +}; + +subtest 'check non public reports are not displayed on around page' => sub { $mech->get_ok('/'); FixMyStreet::override_config { ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], @@ -128,7 +137,7 @@ subtest 'check non public reports are not displayed on around page' => sub { }; -subtest 'check category and status filtering works on /ajax' => sub { +subtest 'check category and status filtering works on /around?ajax' => sub { my $categories = [ 'Pothole', 'Vegetation', 'Flytipping' ]; my $params = { postcode => 'OX1 1ND', @@ -150,21 +159,35 @@ subtest 'check category and status filtering works on /ajax' => sub { } } - my $json = $mech->get_ok_json( '/ajax?bbox=' . $bbox ); + my $json = $mech->get_ok_json( '/around?ajax=1&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 ); + $json = $mech->get_ok_json( '/around?ajax=1&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 ); + $json = $mech->get_ok_json( '/around?ajax=1&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 ); + $json = $mech->get_ok_json( '/around?ajax=1&status=fixed&filter_category=Vegetation&bbox=' . $bbox ); $pins = $json->{pins}; is scalar @$pins, 1, 'correct number of fixed Vegetation reports'; }; +subtest 'check skip_around skips around page' => sub { + my $cobrand = Test::MockModule->new('FixMyStreet::Cobrand::Default'); + $cobrand->mock('skip_around_page', sub { 1 }); + $cobrand->mock('country', sub { 1 }); + + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get('/around?latitude=51.754926&longitude=-1.256179'); + is $mech->res->code, 302, "around page is a redirect"; + is $mech->uri->path, '/report/new', "and redirects to /report/new"; + }; +}; + done_testing(); diff --git a/t/app/controller/auth.t b/t/app/controller/auth.t index cb7d16969..8d60137a2 100644 --- a/t/app/controller/auth.t +++ b/t/app/controller/auth.t @@ -4,7 +4,6 @@ use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; my $test_email = 'test@example.com'; -my $test_email2 = 'test@example.net'; my $test_email3 = 'newuser@example.org'; my $test_password = 'foobar'; @@ -41,8 +40,8 @@ for my $test ( $mech->submit_form_ok( { form_name => 'general_auth', - fields => { email => $email, }, - button => 'email_sign_in', + fields => { username => $email, }, + button => 'sign_in_by_code', }, "try to create an account with email '$email'" ); @@ -60,8 +59,8 @@ $mech->get_ok('/auth'); $mech->submit_form_ok( { form_name => 'general_auth', - fields => { email => $test_email, }, - button => 'email_sign_in', + fields => { username => $test_email, password_register => $test_password }, + button => 'sign_in_by_code', }, "create an account for '$test_email'" ); @@ -101,125 +100,6 @@ $mech->not_logged_in_ok; $mech->log_out_ok; } -# get a sign in email and change password -{ - $mech->clear_emails_ok; - $mech->get_ok('/auth'); - $mech->submit_form_ok( - { - form_name => 'general_auth', - fields => { - email => "$test_email", - r => 'faq', # Just as a test - }, - button => 'email_sign_in', - }, - "email_sign_in with '$test_email'" - ); - - # rest is as before so no need to test - - # follow link and change password - check not prompted for old password - $mech->not_logged_in_ok; - - my $link = $mech->get_link_from_email; - $mech->get_ok($link); - is $mech->uri->path, '/faq', "redirected to the Help page"; - - $mech->get_ok('/auth/change_password'); - - ok my $form = $mech->form_name('change_password'), - "found change password form"; - is_deeply [ sort grep { $_ } map { $_->name } $form->inputs ], # - [ 'confirm', 'new_password', 'token' ], - "check we got expected fields (ie not old_password)"; - - # check the various ways the form can be wrong - for my $test ( - { new => '', conf => '', err => 'enter a password', }, - { new => 'secret', conf => '', err => 'do not match', }, - { new => '', conf => 'secret', err => 'do not match', }, - { new => 'secret', conf => 'not_secret', err => 'do not match', }, - ) - { - $mech->get_ok('/auth/change_password'); - $mech->content_lacks( $test->{err}, "did not find expected error" ); - $mech->submit_form_ok( - { - form_name => 'change_password', - fields => - { new_password => $test->{new}, confirm => $test->{conf}, }, - }, - "change_password with '$test->{new}' and '$test->{conf}'" - ); - $mech->content_contains( $test->{err}, "found expected error" ); - } - - my $user = - FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); - ok $user, "got a user"; - ok !$user->password, "user has no password"; - - $mech->get_ok('/auth/change_password'); - $mech->submit_form_ok( - { - form_name => 'change_password', - fields => - { new_password => $test_password, confirm => $test_password, }, - }, - "change_password with '$test_password' and '$test_password'" - ); - is $mech->uri->path, '/auth/change_password', - "still on change password page"; - $mech->content_contains( 'password has been changed', - "found password changed" ); - - $user->discard_changes(); - 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'); @@ -227,11 +107,11 @@ foreach my $remember_me ( '1', '0' ) { { form_name => 'general_auth', fields => { - email => $test_email, + username => $test_email, password_sign_in => $test_password, remember_me => ( $remember_me ? 1 : undef ), }, - button => 'sign_in', + button => 'sign_in_by_password', }, "sign in with '$test_email' & '$test_password'" ); @@ -253,15 +133,15 @@ $mech->submit_form_ok( { form_name => 'general_auth', fields => { - email => $test_email, + username => $test_email, password_sign_in => 'not the password', }, - button => 'sign_in', + button => 'sign_in_by_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' ); +$mech->content_contains( 'problem with your login information', 'found error message' ); subtest "sign in but have email form autofilled" => sub { $mech->get_ok('/auth'); @@ -269,11 +149,11 @@ subtest "sign in but have email form autofilled" => sub { { form_name => 'general_auth', fields => { - email => $test_email, + username => $test_email, password_sign_in => $test_password, name => 'Auto-completed from elsewhere', }, - button => 'sign_in', + button => 'sign_in_by_password', }, "sign in with '$test_email' and auto-completed name" ); @@ -289,10 +169,10 @@ subtest "sign in with uppercase email" => sub { { form_name => 'general_auth', fields => { - email => $uc_test_email, + username => $uc_test_email, password_sign_in => $test_password, }, - button => 'sign_in', + button => 'sign_in_by_password', }, "sign in with '$uc_test_email' and auto-completed name" ); @@ -317,8 +197,8 @@ FixMyStreet::override_config { $mech->submit_form_ok( { form_name => 'general_auth', - fields => { email => $test_email3, }, - button => 'email_sign_in', + fields => { username => $test_email3, }, + button => 'sign_in_by_code', }, "create a new account" ); @@ -338,13 +218,13 @@ FixMyStreet::override_config { { form_name => 'general_auth', fields => { - email => "$test_email", + username => "$test_email", password_register => $new_password, r => 'faq', # Just as a test }, - button => 'email_sign_in', + button => 'sign_in_by_code', }, - "email_sign_in with '$test_email'" + "sign_in_by_code with '$test_email'" ); $mech->not_logged_in_ok; @@ -361,13 +241,38 @@ FixMyStreet::override_config { { form_name => 'general_auth', fields => { - email => $test_email, + username => $test_email, password_sign_in => $new_password, }, - button => 'sign_in', + button => 'sign_in_by_password', }, "sign in with '$test_email' and new password" ); is $mech->uri->path, '/my', "redirected to correct page"; }; }; + +subtest "check logging in with token" => sub { + $mech->log_out_ok; + $mech->not_logged_in_ok; + + my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + # token needs to be 18 characters + $user->set_extra_metadata('access_token', '1234567890abcdefgh'); + $user->update(); + + $mech->add_header('Authorization', 'Bearer 1234567890abcdefgh'); + $mech->logged_in_ok; + + $mech->delete_header('Authorization'); + $mech->not_logged_in_ok; + + $mech->get_ok('/auth/check_auth?access_token=1234567890abcdefgh'); + + $mech->add_header('Authorization', 'Bearer 1234567890abcdefgh'); + $user->set_extra_metadata('access_token', 'XXXXXXXXXXXXXXXXXX'); + $user->update(); + $mech->not_logged_in_ok; + + $mech->delete_header('Authorization'); +}; diff --git a/t/app/controller/auth_phone.t b/t/app/controller/auth_phone.t new file mode 100644 index 000000000..dea1c3493 --- /dev/null +++ b/t/app/controller/auth_phone.t @@ -0,0 +1,90 @@ +use FixMyStreet::TestMech; + +use t::Mock::Twilio; + +my $twilio = t::Mock::Twilio->new; +LWP::Protocol::PSGI->register($twilio->to_psgi_app, host => 'api.twilio.com'); + +my $mech = FixMyStreet::TestMech->new; + +FixMyStreet::override_config { + SMS_AUTHENTICATION => 1, + PHONE_COUNTRY => 'GB', + TWILIO_ACCOUNT_SID => 'AC123', +}, sub { + + subtest 'Log in with invalid number, fail' => sub { + $mech->get_ok('/auth'); + $mech->submit_form_ok({ + form_name => 'general_auth', + fields => { username => '01214960000000' }, + button => 'sign_in_by_code', + }, "sign in using bad number"); + $mech->content_contains('Please check your phone number is correct'); + }; + + subtest 'Log in using landline, fail' => sub { + $mech->get_ok('/auth'); + $mech->submit_form_ok({ + form_name => 'general_auth', + fields => { username => '01214960000' }, + button => 'sign_in_by_code', + }, "sign in using landline"); + $mech->content_contains('Please enter a mobile number'); + }; + + subtest 'Log in using number that fails at Twilio' => sub { + $mech->get_ok('/auth'); + $mech->submit_form_ok({ + form_name => 'general_auth', + fields => { username => '+18165550101' }, + button => 'sign_in_by_code', + }, "sign in using failing number"); + $mech->content_contains('Sending a confirmation text failed'); + }; + + subtest 'Log in using mobile, by text' => sub { + $mech->submit_form_ok({ + form_name => 'general_auth', + fields => { username => '+18165550100', password_register => 'secret' }, + button => 'sign_in_by_code', + }, "sign in using mobile"); + + $mech->submit_form_ok({ + with_fields => { code => '00000' } + }, 'submit incorrect code'); + $mech->content_contains('Try again'); + + my $code = $twilio->get_text_code; + $mech->submit_form_ok({ + with_fields => { code => $code } + }, 'submit correct code'); + + my $user = FixMyStreet::App->model('DB::User')->find( { phone => '+18165550100' } ); + ok $user, "user created"; + is $mech->uri->path, '/my', "redirected to the 'my' section of site"; + $mech->logged_in_ok; + $mech->log_out_ok; + }; + + subtest 'Log in using mobile, by password' => sub { + $mech->get_ok('/auth'); + $mech->submit_form_ok({ + form_name => 'general_auth', + fields => { username => '+18165550100', password_sign_in => 'incorrect' }, + button => 'sign_in_by_password', + }, "sign in using wrong password"); + $mech->content_contains('There was a problem'); + $mech->submit_form_ok({ + form_name => 'general_auth', + fields => { username => '+18165550100', password_sign_in => 'secret' }, + button => 'sign_in_by_password', + }, "sign in using password"); + + is $mech->uri->path, '/my', "redirected to the 'my' section of site"; + $mech->logged_in_ok; + }; + +}; + +done_testing(); diff --git a/t/app/controller/auth_profile.t b/t/app/controller/auth_profile.t new file mode 100644 index 000000000..74edccfe6 --- /dev/null +++ b/t/app/controller/auth_profile.t @@ -0,0 +1,355 @@ +use FixMyStreet::TestMech; +my $mech = FixMyStreet::TestMech->new; + +use t::Mock::Twilio; + +my $twilio = t::Mock::Twilio->new; +LWP::Protocol::PSGI->register($twilio->to_psgi_app, host => 'api.twilio.com'); + +my $test_email = 'test@example.com'; +my $test_email2 = 'test@example.net'; +my $test_password = 'foobar'; + +END { + done_testing(); +} + +# get a sign in email and change password +subtest "Test change password page" => sub { + $mech->clear_emails_ok; + $mech->get_ok('/auth'); + $mech->submit_form_ok( + { + form_name => 'general_auth', + fields => { + username => $test_email, + r => 'faq', # Just as a test + }, + button => 'sign_in_by_code', + }, + "sign_in_by_code with '$test_email'" + ); + + # follow link and change password - check not prompted for old password + $mech->not_logged_in_ok; + + my $link = $mech->get_link_from_email; + $mech->get_ok($link); + is $mech->uri->path, '/faq', "redirected to the Help page"; + + $mech->get_ok('/auth/change_password'); + + ok my $form = $mech->form_name('change_password'), + "found change password form"; + is_deeply [ sort grep { $_ } map { $_->name } $form->inputs ], # + [ 'confirm', 'new_password', 'token' ], + "check we got expected fields (ie not old_password)"; + + # check the various ways the form can be wrong + for my $test ( + { new => '', conf => '', err => 'enter a password', }, + { new => 'secret', conf => '', err => 'do not match', }, + { new => '', conf => 'secret', err => 'do not match', }, + { new => 'secret', conf => 'not_secret', err => 'do not match', }, + ) + { + $mech->get_ok('/auth/change_password'); + $mech->content_lacks( $test->{err}, "did not find expected error" ); + $mech->submit_form_ok( + { + form_name => 'change_password', + fields => + { new_password => $test->{new}, confirm => $test->{conf}, }, + }, + "change_password with '$test->{new}' and '$test->{conf}'" + ); + $mech->content_contains( $test->{err}, "found expected error" ); + } + + my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + ok $user, "got a user"; + ok !$user->password, "user has no password"; + + $mech->get_ok('/auth/change_password'); + $mech->submit_form_ok( + { + form_name => 'change_password', + fields => + { new_password => $test_password, confirm => $test_password, }, + }, + "change_password with '$test_password' and '$test_password'" + ); + is $mech->uri->path, '/auth/change_password', + "still on change password page"; + $mech->content_contains( 'password has been changed', + "found password changed" ); + + $user->discard_changes(); + ok $user->password, "user now has a password"; +}; + +subtest "Test change email page" => sub { + $mech->create_problems_for_body(1, 2514, 'Title1', { user => FixMyStreet::DB->resultset('User')->find( { email => $test_email } ) } ); + + # 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, '/my', "redirected to /my page"; + $mech->content_contains('successfully confirmed'); + ok(FixMyStreet::App->model('DB::User')->find( { email => $test_email2 } ), "got a user"); + + my $p = FixMyStreet::DB->resultset("Problem")->first; + is $p->user->email, $test_email2, 'problem user updated'; + + my $user1 = FixMyStreet::App->model('DB::User')->create( { email => $test_email, email_verified => 1 } ); + ok($user1, "created old user"); + $mech->create_problems_for_body(1, 2514, 'Title1', { user => $user1 } ); + + $mech->follow_link_ok({url => '/auth/change_email'}); + $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, '/my', "redirected to /my page"; + $mech->content_contains('successfully confirmed'); + + for (FixMyStreet::DB->resultset("Problem")->all) { + is $_->user->email, $test_email; + } + + # Test you can't click the link if logged out + $mech->follow_link_ok({url => '/auth/change_email'}); + $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'); +}; + +my $test_phone_bad = '01214960000000'; +my $test_landline = '01214960000'; +my $test_mobile = '+61491570156'; +my $test_mobile2 = '+61491570157'; + +my $user_mob2 = FixMyStreet::App->model('DB::User')->create( { + phone => $test_mobile, + phone_verified => 1, + name => 'Aus Mobile user', +} ); +$mech->create_problems_for_body(1, 2514, 'Title1', { user => $user_mob2 } ); + +subtest "Test add/verify/change phone page" => sub { + $mech->get_ok('/auth'); + $mech->submit_form_ok({ + with_fields => { + username => $test_email, + password_sign_in => $test_password, + }, + }); + + $mech->follow_link_ok({url => '/auth/change_phone'}); + $mech->submit_form_ok( { with_fields => { username => "" } }, "submit blank change phone form" ); + is $mech->uri->path, '/my', 'redirected'; + $mech->content_contains('successfully removed'); + + $mech->follow_link_ok({url => '/auth/change_phone'}); + $mech->submit_form_ok({ with_fields => { username => $test_phone_bad } }); + $mech->content_contains( 'Please check your phone number is correct', "found expected error" ); + + FixMyStreet::override_config({ + SMS_AUTHENTICATION => 1, + PHONE_COUNTRY => 'GB', + }, sub { + $mech->submit_form_ok({ with_fields => { username => $test_landline } }); + }); + is $mech->uri->path, '/my', 'redirected'; + $mech->content_contains('successfully added'); + + FixMyStreet::override_config({ + SMS_AUTHENTICATION => 1, + PHONE_COUNTRY => 'GB', + }, sub { + $mech->follow_link_ok({url => '/auth/verify/phone'}); + $mech->submit_form_ok({ with_fields => { username => $test_landline } }); + }); + $mech->content_contains( 'Please enter a mobile number', "found expected error" ); + + FixMyStreet::override_config({ + SMS_AUTHENTICATION => 1, + TWILIO_ACCOUNT_SID => 'AC123', + }, sub { + $mech->submit_form_ok({ with_fields => { username => $test_mobile } }); + }); + is $mech->uri->path, '/auth/verify/phone', "still on change phone page"; + $mech->content_contains( 'Now check your phone', "found check your phone" ); + + $mech->submit_form_ok({ + with_fields => { code => '00000' } + }, 'submit incorrect code'); + $mech->content_contains('Try again'); + + my $code = $twilio->get_text_code; + $mech->submit_form_ok({ + with_fields => { code => $code } + }, 'submit correct code'); + + my $user = FixMyStreet::App->model('DB::User')->find( { phone => $test_mobile } ); + ok $user, "user exists"; + is $user->email_verified, 1; + is $user->email, $test_email, 'email still same'; + is $mech->uri->path, '/my', "redirected to /my page"; + $mech->content_contains('successfully verified'); + $mech->logged_in_ok; +}; + +subtest "Test change phone to existing account" => sub { + $mech->get_ok('/auth'); + FixMyStreet::override_config({ + SMS_AUTHENTICATION => 1, + }, sub { + $mech->submit_form_ok({ + with_fields => { + username => $test_mobile, + password_sign_in => $test_password, + }, + }); + }); + + $mech->follow_link_ok({url => '/auth/change_phone'}); + + FixMyStreet::override_config({ + SMS_AUTHENTICATION => 1, + TWILIO_ACCOUNT_SID => 'AC123', + }, sub { + $mech->submit_form_ok({ with_fields => { username => $test_mobile2 } }); + }); + is $mech->uri->path, '/auth/change_phone', "still on change phone page"; + $mech->content_contains( 'Now check your phone', "found check your phone" ); + + my $code = $twilio->get_text_code; + $mech->submit_form_ok({ with_fields => { code => $code } }, 'submit correct code'); + + my $user = FixMyStreet::App->model('DB::User')->find( { phone => $test_mobile } ); + ok !$user, 'old user does not exist'; + $user = FixMyStreet::App->model('DB::User')->find( { phone => $test_mobile2 } ); + ok $user, "new mobile user exists"; + is $user->email_verified, 1; + is $user->email, $test_email, 'email still same'; + is $mech->uri->path, '/my', "redirected to /my page"; + $mech->content_contains('successfully verified'); + + for (FixMyStreet::DB->resultset("Problem")->all) { + is $_->user->email, $test_email; + } +}; + +subtest "Test superuser can access generate token page" => sub { + my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + ok $user->update({ is_superuser => 0 }), 'user not superuser'; + + $mech->log_out_ok; + $mech->get_ok('/auth'); + $mech->submit_form_ok({ + with_fields => { + username => $test_email, + password_sign_in => $test_password, + }, + }); + + $mech->content_lacks('Generate token'); + + $mech->get('/auth/generate_token'); + is $mech->res->code, 403, "access denied"; + + ok $user->update({ is_superuser => 1 }), 'user is superuser'; + + $mech->get_ok('/my'); + $mech->content_contains('Generate token'); + $mech->get_ok('/auth/generate_token'); +}; + +subtest "Test staff user can access generate token page" => sub { + my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + ok $user->update({ is_superuser => 0 }), 'user not superuser'; + + $mech->log_out_ok; + $mech->get_ok('/auth'); + $mech->submit_form_ok({ + with_fields => { + username => $test_email, + password_sign_in => $test_password, + }, + }); + + $mech->content_lacks('Generate token'); + + my $body = $mech->create_body_ok(2237, 'Oxfordshire'); + + $mech->get('/auth/generate_token'); + is $mech->res->code, 403, "access denied"; + + ok $user->update({ from_body => $body }), 'user is staff user'; + + $mech->get_ok('/my'); + $mech->content_contains('Generate token'); + $mech->get_ok('/auth/generate_token'); +}; + +subtest "Test generate token page" => sub { + my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); + ok $user->update({ is_superuser => 1 }), 'user set to superuser'; + + $mech->log_out_ok; + + $mech->get_ok('/auth'); + $mech->submit_form_ok({ + with_fields => { + username => $test_email, + password_sign_in => $test_password, + }, + }); + + ok !$user->get_extra_metadata('access_token'); + + $mech->get_ok('/my'); + $mech->follow_link_ok({url => '/auth/generate_token'}); + $mech->content_lacks('Token:'); + $mech->submit_form_ok( + { with_fields => { generate_token => 'Generate token' } }, + "submit generate token form" + ); + $mech->content_contains( 'Your token has been generated', "token generated" ); + + $user->discard_changes(); + my $token = $user->get_extra_metadata('access_token'); + ok $token, 'access token set'; + + $mech->content_contains($token, 'access token displayed'); + + $mech->get_ok('/auth/generate_token'); + $mech->content_contains('Current token:'); + $mech->content_contains($token, 'access token displayed'); + $mech->content_contains('If you generate a new token'); + + $mech->log_out_ok; + $mech->add_header('Authorization', "Bearer $token"); + $mech->logged_in_ok; +} diff --git a/t/app/controller/auth_social.t b/t/app/controller/auth_social.t index 726d264bd..031fb8d9e 100644 --- a/t/app/controller/auth_social.t +++ b/t/app/controller/auth_social.t @@ -102,11 +102,7 @@ for my $fb_state ( 'refused', 'no email', 'existing UID', 'okay' ) { $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->{username} = $fb_email; $fields->{name} = 'Ffion Tester'; $mech->submit_form(with_fields => $fields); $mech->content_contains('Nearly done! Now check your email'); @@ -214,11 +210,7 @@ for my $tw_state ( 'refused', 'existing UID', '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->{username} = $tw_email; $fields->{name} = 'Ffion Tester'; $mech->submit_form(with_fields => $fields); $mech->content_contains('Nearly done! Now check your email'); diff --git a/t/app/controller/dashboard.t b/t/app/controller/dashboard.t index 457eceade..b53056968 100644 --- a/t/app/controller/dashboard.t +++ b/t/app/controller/dashboard.t @@ -1,600 +1,150 @@ use Test::MockTime ':all'; +use strict; +use warnings; use FixMyStreet::TestMech; use Web::Scraper; -my $mech = FixMyStreet::TestMech->new; +set_absolute_time('2014-02-01T12:00:00'); -my $test_user = 'council_user@example.com'; -my $test_pass = 'password'; -my $test_council = 2651; -my $test_ward = 20723; +my $mech = FixMyStreet::TestMech->new; -my $body = $mech->create_body_ok($test_council, 'City of Edinburgh Council'); +my $other_body = $mech->create_body_ok(1234, 'Some Other Council'); +my $body = $mech->create_body_ok(2651, 'City of Edinburgh Council'); +my @cats = ('Litter', 'Other', 'Potholes', 'Traffic lights'); +for my $contact ( @cats ) { + $mech->create_contact_ok(body_id => $body->id, category => $contact, email => "$contact\@example.org"); +} -$mech->delete_user( $test_user ); -my $user = $mech->create_user_ok($test_user, password => $test_pass); +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); +my $counciluser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $body); +my $normaluser = $mech->create_user_ok('normaluser@example.com', name => 'Normal User'); -my $p_user = $mech->create_user_ok('p_user@example.com'); +my $body_id = $body->id; +my $area_id = '60705'; +my $alt_area_id = '62883'; -# 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 { +my $last_month = DateTime->now->subtract(months => 2); +$mech->create_problems_for_body(2, $body->id, 'Title', { areas => ",$area_id,2651,", category => 'Potholes', cobrand => 'fixmystreet' }); +$mech->create_problems_for_body(3, $body->id, 'Title', { areas => ",$area_id,2651,", category => 'Traffic lights', cobrand => 'fixmystreet', dt => $last_month }); +$mech->create_problems_for_body(1, $body->id, 'Title', { areas => ",$alt_area_id,2651,", category => 'Litter', cobrand => 'fixmystreet' }); - $mech->not_logged_in_ok; - $mech->get_ok('/dashboard'); +my @scheduled_problems = $mech->create_problems_for_body(7, $body->id, 'Title', { areas => ",$area_id,2651,", category => 'Traffic lights', cobrand => 'fixmystreet' }); +my @fixed_problems = $mech->create_problems_for_body(4, $body->id, 'Title', { areas => ",$area_id,2651,", category => 'Potholes', cobrand => 'fixmystreet' }); +my @closed_problems = $mech->create_problems_for_body(3, $body->id, 'Title', { areas => ",$area_id,2651,", category => 'Traffic lights', cobrand => 'fixmystreet' }); - $mech->content_contains( 'sign in' ); - - $mech->submit_form( - with_fields => { email => $test_user, password_sign_in => $test_pass } - ); - - is $mech->status, '404', 'If not council user get 404'; +foreach my $problem (@scheduled_problems) { + $problem->update({ state => 'action scheduled' }); + $mech->create_comment_for_problem($problem, $counciluser, 'Title', 'text', 0, 'confirmed', 'action scheduled'); +} - $user->from_body( $body->id ); - $user->update; +foreach my $problem (@fixed_problems) { + $problem->update({ state => 'fixed - council' }); + $mech->create_comment_for_problem($problem, $counciluser, 'Title', 'text', 0, 'confirmed', 'fixed'); +} - $mech->log_out_ok; - $mech->get_ok('/dashboard'); - $mech->submit_form_ok( { - with_fields => { email => $test_user, password_sign_in => $test_pass } - } ); +foreach my $problem (@closed_problems) { + $problem->update({ state => 'closed' }); + $mech->create_comment_for_problem($problem, $counciluser, 'Title', 'text', 0, 'confirmed', 'closed', { confirmed => \'current_timestamp' }); +} - $mech->content_contains( 'Area 2651' ); +my $categories = scraper { + process "select[name=category] > option", 'cats[]' => 'TEXT', + process "table[id=overview] > tr", 'rows[]' => scraper { + process 'td', 'cols[]' => 'TEXT' + }, +}; - FixMyStreet::App->model('DB::Contact')->search( { body_id => $body->id } ) - ->delete; +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', +}, sub { - delete_problems(); + subtest 'not logged in, redirected to login' => sub { + $mech->not_logged_in_ok; + $mech->get_ok('/dashboard'); + $mech->content_contains( 'sign in' ); + }; - 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", - state => 'confirmed', - whenedited => DateTime->now, - editor => 'test', - note => 'test', - } - ); - } + subtest 'normal user, 404' => sub { + $mech->log_in_ok( $normaluser->email ); + $mech->get('/dashboard'); + is $mech->status, '404', 'If not council user get 404'; + }; - $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=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' - }, + subtest 'superuser, body list' => sub { + $mech->log_in_ok( $superuser->email ); + $mech->get_ok('/dashboard'); + # Contains body name, in list of bodies + $mech->content_contains('Some Other Council'); + $mech->content_contains('Edinburgh Council'); + $mech->content_lacks('Category:'); + $mech->get_ok('/dashboard?body=' . $body->id); + $mech->content_lacks('Some Other Council'); + $mech->content_contains('Edinburgh Council'); + $mech->content_contains('Trowbridge'); + $mech->content_contains('Category:'); }; - my $expected_cats = [ 'All', '-- Pick a category --', @cats ]; - my $res = $categories->scrape( $mech->content ); - is_deeply( $res->{cats}, $expected_cats, 'correct list of categories' ); + subtest 'council user, ward list' => sub { + $mech->log_in_ok( $counciluser->email ); + $mech->get_ok('/dashboard'); + $mech->content_lacks('Some Other Council'); + $mech->content_contains('Edinburgh Council'); + $mech->content_contains('Trowbridge'); + $mech->content_contains('Category:'); + }; - foreach my $row ( @{ $res->{rows} }[1 .. 11] ) { - foreach my $col ( @{ $row->{cols} } ) { - is $col, 0; - } - } + subtest 'area user can only see their area' => sub { + $counciluser->update({area_id => $area_id}); - for my $reports ( @{ $res->{report_lists} } ) { - is_deeply $reports, {}, 'No reports'; - } + $mech->get_ok("/dashboard"); + $mech->content_contains('<h1>Trowbridge</h1>'); + $mech->get_ok("/dashboard?body=" . $other_body->id); + $mech->content_contains('<h1>Trowbridge</h1>'); + $mech->get_ok("/dashboard?ward=$alt_area_id"); + $mech->content_contains('<h1>Trowbridge</h1>'); - 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], - }, - { - desc => 'confirmed last 8 days with no state', - dt => $now->clone->subtract( days => 8 ), - counts => [1,2,3,3], - report_counts => [2, 1, 0], - }, - { - desc => 'confirmed last 2 weeks with no state', - dt => $now->clone->subtract( weeks => 2, hours => 1 ), - counts => [1,2,4,4], - report_counts => [2, 1, 1], - }, - { - 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} ); - }; - } + $counciluser->update({area_id => undef}); + }; - 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 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] - } - }, - { - 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] - } - }, - { - 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] - } - }, - { - 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] - } - }, - { - 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] - } - }, - { - 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] - } - }, - { - 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] - } - }, - ) { - 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} ); - } - }; - } + subtest 'The correct categories and totals shown by default' => sub { + $mech->get_ok("/dashboard"); + my $expected_cats = [ 'All', @cats ]; + my $res = $categories->scrape( $mech->content ); + is_deeply( $res->{cats}, $expected_cats, 'correct list of categories' ); + # Three missing as more than a month ago + test_table($mech->content, 1, 0, 0, 1, 0, 0, 0, 0, 2, 0, 4, 6, 7, 3, 0, 10, 10, 3, 4, 17); + }; - 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], - }, - { - 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], - }, - { - 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], - }, - { - 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], - }, - { - 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], - }, - { - 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], - }, - ) { - subtest $test->{desc} => sub { - make_problem( $test->{p1} ) if $test->{p1}; - make_problem( $test->{p2} ) if $test->{p2}; - - $mech->get_ok('/dashboard'); - - $res = $categories->scrape( $mech->content ); - - foreach my $row ( keys %{ $test->{counts} } ) { - check_row( $res, $row, $test->{counts}->{$row} ); - } - - check_report_counts( $res, $test->{report_counts} ); - - $mech->submit_form_ok( { - with_fields => { - category => $test->{category}, - ward => $test->{ward}, - } - } ); - - $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} ); - }; - } + subtest 'test filters' => sub { + $mech->get_ok("/dashboard"); + $mech->submit_form_ok({ with_fields => { category => 'Litter' } }); + test_table($mech->content, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1); + $mech->submit_form_ok({ with_fields => { category => '', state => 'fixed - council' } }); + test_table($mech->content, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 4, 4); + $mech->submit_form_ok({ with_fields => { state => 'action scheduled' } }); + test_table($mech->content, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 7, 7, 0, 0, 7); + my $start = DateTime->now->subtract(months => 3)->strftime('%Y-%m-%d'); + my $end = DateTime->now->subtract(months => 1)->strftime('%Y-%m-%d'); + $mech->submit_form_ok({ with_fields => { state => '', start_date => $start, end_date => $end } }); + test_table($mech->content, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 3, 0, 0, 3); + }; - delete_problems(); - - for my $test ( - { - desc => 'Selecting no state does nothing', - p1 => { - state => 'fixed - user', - conf_dt => DateTime->now()->subtract( minutes => 1 ), - category => 'Potholes', - }, - p2 => { - state => 'confirmed', - conf_dt => DateTime->now()->subtract( minutes => 1 ), - category => 'Litter', - }, - state => '', - report_counts => [2,0,0], - report_counts_after => [2,0,0], - }, - { - desc => 'limit by state works', - state => 'fixed - council', - 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()->subtract( minutes => 1 ), - category => 'Potholes', - }, - p2 => { - state => 'fixed', - conf_dt => DateTime->now()->subtract( minutes => 1 ), - category => 'Potholes', - }, - 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}; - - $mech->get_ok('/dashboard'); - - $res = $categories->scrape( $mech->content ); - - check_report_counts( $res, $test->{report_counts} ); - - $mech->submit_form_ok( { - with_fields => { - state => $test->{state}, - } - } ); - - $res = $categories->scrape( $mech->content ); - - check_report_counts( $res, $test->{report_counts_after} ); - }; - } + subtest 'test grouping' => sub { + $mech->get_ok("/dashboard?group_by=category"); + test_table($mech->content, 1, 0, 6, 10, 17); + $mech->get_ok("/dashboard?group_by=state"); + test_table($mech->content, 3, 7, 4, 3, 17); + $mech->get_ok("/dashboard?start_date=2000-01-01&group_by=month"); + test_table($mech->content, 0, 17, 17, 3, 0, 3, 3, 17, 20); + }; subtest 'export as csv' => sub { - make_problem( { + $mech->create_problems_for_body(1, $body->id, 'Title', { detail => "this report\nis split across\nseveral lines", - state => "confirmed", - conf_dt => DateTime->now(), - areas => 62883, - } ); + areas => ",$alt_area_id,2651,", + }); $mech->get_ok('/dashboard?export=1'); open my $data_handle, '<', \$mech->content; my $csv = Text::CSV->new( { binary => 1 } ); @@ -602,7 +152,7 @@ FixMyStreet::override_config { while ( my $row = $csv->getline( $data_handle ) ) { push @rows, $row; } - is scalar @rows, 6, '1 (header) + 5 (reports) = 6 lines'; + is scalar @rows, 19, '1 (header) + 18 (reports) = 19 lines'; is scalar @{$rows[0]}, 18, '18 columns present'; @@ -621,7 +171,7 @@ FixMyStreet::override_config { 'Status', 'Latitude', 'Longitude', - 'Nearest Postcode', + 'Query', 'Ward', 'Easting', 'Northing', @@ -629,84 +179,40 @@ FixMyStreet::override_config { ], 'Column headers look correct'; - is $rows[5]->[14], 'Bradford-on-Avon', 'Ward column is name not ID'; - - is $rows[5]->[15], '610591', 'Correct Easting conversion'; - is $rows[5]->[16], '126573', 'Correct Northing conversion'; + is $rows[5]->[14], 'Trowbridge', 'Ward column is name not ID'; + is $rows[5]->[15], '529025', 'Correct Easting conversion'; + is $rows[5]->[16], '179716', 'Correct Northing conversion'; }; -}; -restore_time; - -sub make_problem { - my $args = shift; - - my $p = FixMyStreet::App->model('DB::Problem')->create( { - title => 'a problem', - name => 'a user', - anonymous => 1, - detail => $args->{detail} || 'some detail', - state => $args->{state}, - confirmed => $args->{conf_dt}, - whensent => $args->{conf_dt}, - lastupdate => $args->{mark_dt} || $args->{conf_dt}, - bodies_str => $body->id, - postcode => 'EH99 1SP', - latitude => '51', - longitude => '1', - areas => $args->{areas} || $test_ward, - used_map => 0, - user_id => $p_user->id, - category => $args->{category} || 'Other', - } ); - - if ( $args->{state} ne 'confirmed' ) { - my $c = FixMyStreet::App->model('DB::Comment')->create( { - problem => $p, - user_id => $p_user->id, - state => 'confirmed', - problem_state => $args->{state} =~ /^fixed - user|fixed$/ ? undef : $args->{state}, - confirmed => $args->{mark_dt}, - text => 'an update', - mark_fixed => $args->{state} =~ /fixed/ ? 1 : 0, - anonymous => 1, - } ); - } -} -sub check_row { - my $res = shift; - my $row = shift; - my $totals = shift; + subtest 'export as csv using token' => sub { + $mech->log_out_ok; - is $res->{ $row }->[0], $totals->[0], "Correct count in $row for WTD"; - is $res->{ $row }->[1], $totals->[1], "Correct count in $row for last 7 days"; - is $res->{ $row }->[2], $totals->[2], "Correct count in $row for last 4 weeks"; - is $res->{ $row }->[3], $totals->[3], "Correct count in $row for YTD"; -} + $counciluser->set_extra_metadata('access_token', '1234567890abcdefgh'); + $counciluser->update(); + + $mech->get_ok('/dashboard?export=1'); + like $mech->res->header('Content-type'), qr'text/html'; + $mech->content_lacks('Report ID'); + + $mech->add_header('Authorization', 'Bearer 1234567890abcdefgh'); + $mech->get_ok('/dashboard?export=1'); + like $mech->res->header('Content-type'), qr'text/csv'; + $mech->content_contains('Report ID'); + }; +}; -sub check_report_counts { - my $res = shift; - my $counts = shift; - - for my $i ( 0 .. 2 ) { - if ( $counts->[$i] == 0 ) { - is_deeply $res->{report_lists}->[$i], {}, "No reports for column $i"; - } else { - if ( ref( $res->{report_lists}->[$i]->{reports} ) eq 'ARRAY' ) { - is scalar @{ $res->{report_lists}->[$i]->{reports} }, $counts->[$i], "Correct report count for column $i"; - } else { - fail "Correct report count for column $i ( no reports )"; - } +sub test_table { + my ($content, @expected) = @_; + my $res = $categories->scrape( $mech->content ); + my $i = 0; + foreach my $row ( @{ $res->{rows} }[1 .. 11] ) { + foreach my $col ( @{ $row->{cols} } ) { + is $col, $expected[$i++]; } } } -sub delete_problems { - FixMyStreet::App->model('DB::Comment') - ->search( { 'problem.bodies_str' => $body->id }, { join => 'problem' } ) - ->delete; - FixMyStreet::App->model('DB::Problem') - ->search( { bodies_str => $body->id } )->delete(); +END { + restore_time; + done_testing(); } - -done_testing; diff --git a/t/app/controller/index.t b/t/app/controller/index.t index be4da6034..9be6dfa1e 100644 --- a/t/app/controller/index.t +++ b/t/app/controller/index.t @@ -8,7 +8,11 @@ subtest "check that the form goes to /around" => sub { $mech->get_ok('/'); is $mech->uri->path, '/', "still on '/'"; - $mech->submit_form_ok( { with_fields => { pc => 'SW1A 1AA', } } ); + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/' + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'SW1A 1AA', } } ); + }; # check that we are at /around is $mech->uri->path, '/around', "Got to /around"; diff --git a/t/app/controller/moderate.t b/t/app/controller/moderate.t index c3c77866b..4b2f0cfe3 100644 --- a/t/app/controller/moderate.t +++ b/t/app/controller/moderate.t @@ -1,3 +1,11 @@ +package FixMyStreet::Cobrand::Tester; + +use parent 'FixMyStreet::Cobrand::Default'; + +sub send_moderation_notifications { 0 } + +package main; + use FixMyStreet::TestMech; use FixMyStreet::App; use Data::Dumper; @@ -176,6 +184,30 @@ subtest 'Problem moderation' => sub { # reset $report->update({ state => 'confirmed' }); }; + + subtest 'Hide report without sending email' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'tester' => '.' } ] + }, sub { + + $mech->clear_emails_ok; + + $mech->get_ok($REPORT_URL); + $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'; + + ok $mech->email_count_is(0), "Email wasn't sent"; + + # reset + $report->update({ state => 'confirmed' }); + } + }; }; $mech->content_lacks('Posted anonymously', 'sanity check'); @@ -337,7 +369,7 @@ subtest 'And do it as a superuser' => sub { problem_title => 'Good good', problem_detail => 'Good good improved', }}); - $mech->content_contains('Moderated by a FixMyStreet administrator'); + $mech->content_contains('Moderated by an administrator'); }; done_testing(); diff --git a/t/app/controller/my_planned.t b/t/app/controller/my_planned.t index 51ea0297e..67d59e148 100644 --- a/t/app/controller/my_planned.t +++ b/t/app/controller/my_planned.t @@ -5,6 +5,7 @@ $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 County Council'); +my $body2 = $mech->create_body_ok(2421, 'Oxford City Council'); my ($problem) = $mech->create_problems_for_body(1, $body->id, 'Test Title'); $mech->get_ok($problem->url); @@ -77,4 +78,155 @@ subtest "POSTing multiple problems to my/planned/change adds all to shortlist" = $mech->text_contains('Shortlisted'); }; +subtest "re-ordering shortlist on non shortlist page redirect to shortlist" => sub { + $user->user_planned_reports->remove(); + my ($problem1) = $mech->create_problems_for_body(1, $body->id, 'New Problem'); + + $mech->get_ok($problem1->url); + my ($csrf) = $mech->content =~ /meta content="([^"]*)" name="csrf-token"/; + + $mech->post_ok( '/my/planned/change', { + id => $problem1->id, + 'shortlist-up' => 1, + token => $csrf, + }, + ); + + $mech->content_contains('Your shortlist'); +}; + +subtest "shortlist with no action is forbidden" => sub { + $user->user_planned_reports->remove(); + my ($problem1) = $mech->create_problems_for_body(1, $body->id, 'New Problem'); + + $mech->get_ok($problem1->url); + my ($csrf) = $mech->content =~ /meta content="([^"]*)" name="csrf-token"/; + + my $result = $mech->post( '/my/planned/change', { + id => $problem1->id, + token => $csrf, + }, + ); + + is $result->code, 403, '403 response if no action'; +}; + +subtest "cannot remove non-existant problems from shortlist" => sub { + $user->user_planned_reports->remove(); + my ($problem1) = $mech->create_problems_for_body(1, $body->id, 'New Problem'); + + $mech->get_ok($problem1->url); + my ($csrf) = $mech->content =~ /meta content="([^"]*)" name="csrf-token"/; + + my $result = $mech->post( '/my/planned/change', { + id => 999, + 'shortlist-remove' => 1, + token => $csrf, + }, + ); + + is $result->code, 404, 'removing missing report returns 404'; +}; + +subtest "can remove problems from shortlist" => sub { + $user->user_planned_reports->remove(); + my ($problem1, $problem2) = $mech->create_problems_for_body(2, $body->id, 'New Problem'); + + $mech->get_ok($problem1->url); + my ($csrf) = $mech->content =~ /meta content="([^"]*)" name="csrf-token"/; + + $mech->post_ok( '/my/planned/change_multiple', { + 'ids[]' => [ + $problem1->id, + $problem2->id, + ], + token => $csrf, + } + ); + + $mech->get_ok($problem1->url); + $mech->text_contains('Shortlisted'); + + ($csrf) = $mech->content =~ /meta content="([^"]*)" name="csrf-token"/; + + $mech->post_ok( '/my/planned/change', { + id => $problem1->id, + 'shortlist-remove' => 1, + token => $csrf, + }, + 'Removed problem from shortlist'); + + $mech->get_ok($problem1->url); + $mech->text_lacks('Shortlisted'); + $mech->text_contains('Shortlist'); + + # check cases where problem has changed body due + # to e.g. change of category + $problem2->update({ + bodies_str => $body2->id, + title => 'Other body problem', + }); + + $mech->get_ok('/my/planned'); + $mech->text_contains('Other body problem'); + + ($csrf) = $mech->content =~ /meta content="([^"]*)" name="csrf-token"/; + + $mech->post_ok( '/my/planned/change', { + id => $problem2->id, + 'shortlist-remove' => 1, + token => $csrf, + }, + 'Removed problem for other body from shortlist'); + + $mech->get_ok('/my/planned'); + $mech->text_lacks('Other body problem'); +}; + +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + BASE_URL => 'http://oxfordshire.fixmystreet.site', +}, sub { + subtest "can remove problems not displayed in cobrand from shortlist" => sub { + $user->user_planned_reports->remove(); + my ($problem1) = $mech->create_problems_for_body(2, $body->id, 'New Problem'); + + $mech->get_ok($problem1->url); + my ($csrf) = $mech->content =~ /meta content="([^"]*)" name="csrf-token"/; + + $mech->post_ok( '/my/planned/change_multiple', { + 'ids[]' => [ + $problem1->id, + ], + token => $csrf, + } + ); + + $mech->get_ok('/my/planned'); + $mech->text_contains('New Problem'); + $mech->content_contains('Remove from shortlist'); + + $problem1->update({ + bodies_str => $body2->id, + }); + + $mech->get_ok('/my/planned'); + $mech->text_contains('New Problem'); + $mech->content_contains('Remove from shortlist'); + + ($csrf) = $mech->content =~ /meta content="([^"]*)" name="csrf-token"/; + + $mech->post_ok( '/my/planned/change', { + id => $problem1->id, + 'shortlist-remove' => 1, + token => $csrf, + ajax => 1, + }, + 'Removed problem not displayed in this cobrand'); + + $mech->get_ok('/my/planned'); + $mech->text_lacks('New Problem', 'Problem no longer in shortlist'); + }; +}; + done_testing(); diff --git a/t/app/controller/report_as_other.t b/t/app/controller/report_as_other.t index daa213e8c..91644e8ce 100644 --- a/t/app/controller/report_as_other.t +++ b/t/app/controller/report_as_other.t @@ -47,7 +47,7 @@ subtest "Body user, has permission to add report as another user" => sub { detail => 'Test report details.', category => 'Potholes', name => 'Another User', - email => 'another@example.net', + username => 'another@example.net', ); is $report->name, 'Another User', 'report name is given name'; is $report->user->name, 'Another User', 'user name matches'; @@ -66,7 +66,7 @@ subtest "Body user, has permission to add report as another (existing) user" => detail => 'Test report details.', category => 'Potholes', name => 'Existing Yooser', - email => 'existing@example.net', + username => 'existing@example.net', ); is $report->name, 'Existing Yooser', 'report name is given name'; is $report->user->name, 'Existing User', 'user name remains same'; @@ -108,7 +108,7 @@ subtest "Body user, has permission to add update as another user" => sub { form_as => 'another_user', update => 'Test Update', name => 'Another User', - rznvy => 'another2@example.net', + username => 'another2@example.net', ); is $update->name, 'Another User', 'update name is given name'; is $update->user->name, 'Another User', 'user name matches'; @@ -124,7 +124,7 @@ subtest "Body user, has permission to add update as another (existing) user" => form_as => 'another_user', update => 'Test Update', name => 'Existing Yooser', - rznvy => 'existing@example.net', + username => 'existing@example.net', ); is $update->name, 'Existing Yooser', 'update name is given name'; is $update->user->name, 'Existing User', 'user name remains same'; diff --git a/t/app/controller/report_display.t b/t/app/controller/report_display.t index 4d73a5204..f0913fbd2 100644 --- a/t/app/controller/report_display.t +++ b/t/app/controller/report_display.t @@ -128,7 +128,7 @@ subtest "test a good report" => sub { my %fields = ( name => '', - rznvy => '', + username => '', update => '', add_alert => 1, # defaults to true fixed => undef diff --git a/t/app/controller/report_import.t b/t/app/controller/report_import.t index 47113198e..e4a202db7 100644 --- a/t/app/controller/report_import.t +++ b/t/app/controller/report_import.t @@ -362,14 +362,12 @@ subtest "Submit a correct entry (with location) to cobrand" => sub { photo2 => '', photo3 => '', phone => '', - email => 'test-ll@example.com', + username => 'test-ll@example.com', }, "check imported fields are shown" or diag Dumper( $mech->visible_form_values ); use Data::Dumper; - my $user = - FixMyStreet::App->model('DB::User') - ->find( { email => 'test-ll@example.com' } ); + my $user = FixMyStreet::App->model('DB::User')->find( { email => 'test-ll@example.com' } ); ok $user, "Found a user"; my $report = $user->problems->first; diff --git a/t/app/controller/report_inspect.t b/t/app/controller/report_inspect.t index 5bbbdff79..239cc408b 100644 --- a/t/app/controller/report_inspect.t +++ b/t/app/controller/report_inspect.t @@ -11,10 +11,18 @@ my $rp = FixMyStreet::DB->resultset("ResponsePriority")->create({ body => $oxon, name => 'High Priority', }); +my $rp2 = FixMyStreet::DB->resultset("ResponsePriority")->create({ + body => $oxon, + name => 'Low Priority', +}); FixMyStreet::DB->resultset("ContactResponsePriority")->create({ contact => $contact, response_priority => $rp, }); +FixMyStreet::DB->resultset("ContactResponsePriority")->create({ + contact => $contact3, + response_priority => $rp2, +}); my $wodc = $mech->create_body_ok(2420, 'West Oxfordshire District Council'); $mech->create_contact_ok( body_id => $wodc->id, category => 'Horses', email => 'horses@example.net' ); @@ -116,7 +124,6 @@ FixMyStreet::override_config { $mech->content_contains('Invalid location'); $mech->submit_form_ok({ button => 'save', with_fields => { latitude => 51.754926, longitude => -1.256179, include_update => undef } }); $mech->content_lacks('Invalid location'); - $user->user_body_permissions->search({ body_id => $oxon->id, permission_type => 'planned_reports' })->delete; }; subtest "test duplicate reports are shown" => sub { @@ -140,6 +147,71 @@ FixMyStreet::override_config { $report2->update; }; + subtest "can mark a report as duplicate without supplying a duplicate and a public update" => 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', include_update => "0" } }); + + $mech->content_contains('provide a duplicate ID', "error message about missing duplicate id"); + $report->discard_changes; + $report2->discard_changes; + + is $report->state, $old_state, 'report not marked as duplicate'; + is $report->comments->search({ problem_state => 'duplicate' })->count, 0, 'no update marking report as duplicate was left'; + + is $report->get_extra_metadata('duplicate_of'), undef; + + $mech->submit_form_ok({ button => 'save', with_fields => { state => 'Duplicate', public_update => 'This is a duplicate', include_update => "1" } }); + $mech->content_lacks('provide a duplicate ID', "no error message about missing duplicate id"); + $report->discard_changes; + $report2->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'; + is $report->get_extra_metadata('duplicate_of'), undef; + is_deeply $report2->get_extra_metadata('duplicates'), undef; + + $report->update({ state => $old_state }); + }; + + subtest "can mark a report as duplicate without supplying a public update and a duplicate id" => 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', include_update => "0" } }); + + $mech->content_contains('provide a duplicate ID', "error message about missing duplicate id"); + $report->discard_changes; + $report2->discard_changes; + + is $report->state, $old_state, 'report not marked as duplicate'; + is $report->comments->search({ problem_state => 'duplicate' })->count, 0, 'no update marking report as duplicate was left'; + + is $report->get_extra_metadata('duplicate_of'), undef; + + $mech->submit_form_ok({ button => 'save', with_fields => { state => 'Duplicate', duplicate_of => $report2->id, include_update => "0" } }); + $mech->content_lacks('provide a duplicate ID', "no error message about missing duplicate id"); + $report->discard_changes; + $report2->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'; + is $report->get_extra_metadata('duplicate_of'), $report2->id; + is_deeply $report2->get_extra_metadata('duplicates'), [ $report->id ]; + + # Check that duplicate does not include shortlist add button (no form in form) + $mech->get_ok("/report/$report_id"); + $mech->content_lacks('item-list__item__shortlist-add'); + + $report->set_extra_metadata('duplicate_of', undef); + $report->update({ state => $old_state }); + $report2->set_extra_metadata('duplicates', undef); + $report2->update; + }; + subtest "marking a report as a duplicate with update correctly sets update status" => sub { my $old_state = $report->state; $report->comments->delete_all; @@ -172,6 +244,9 @@ FixMyStreet::override_config { my $old_state = $report->state; $report->comments->delete_all; + $mech->get_ok("/report/$report2_id/nearby.json"); + $mech->content_lacks('Add to shortlist'); + $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 => { @@ -184,6 +259,7 @@ FixMyStreet::override_config { is $report->state, 'duplicate', 'report marked as duplicate'; is $report->comments->search({ problem_state => 'duplicate' })->count, 1, 'update marked report as duplicate'; + $mech->get_ok("/report/$report_id"); # Get again as planned_reports permission means redirect to referer... $mech->content_contains($update_text); $mech->content_lacks("Thank you for your report. This problem has already been reported."); @@ -191,7 +267,6 @@ FixMyStreet::override_config { }; subtest "post-inspect redirect is to the right place if URL set" => sub { - $user->user_body_permissions->create({ body => $oxon, permission_type => 'planned_reports' }); $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 => { @@ -202,11 +277,9 @@ FixMyStreet::override_config { is $mech->res->code, 200, "got 200"; is $mech->res->previous->code, 302, "got 302 for redirect"; is $mech->uri->path, '/', 'redirected to front page'; - $user->user_body_permissions->search({ body_id => $oxon->id, permission_type => 'planned_reports' })->delete; }; subtest "post-inspect redirect is to the right place if URL not set" => sub { - $user->user_body_permissions->create({ body => $oxon, permission_type => 'planned_reports' }); $user->set_extra_metadata(categories => [ $contact->id ]); $user->update; $mech->get_ok("/report/$report_id"); @@ -223,7 +296,6 @@ FixMyStreet::override_config { is $params{lat}, $report->latitude, "latitude param is correct"; is $params{lon}, $report->longitude, "longitude param is correct"; is $params{filter_category}, $contact->category, "categories param is correct"; - $user->user_body_permissions->search({ body_id => $oxon->id, permission_type => 'planned_reports' })->delete; }; subtest "default response priorities display correctly" => sub { @@ -254,15 +326,165 @@ FixMyStreet::override_config { $mech->submit_form_ok({ button => 'save', with_fields => { - $test->{priority} ? (priority => 1) : (), + $test->{priority} ? (priority => $rp->id) : (), $test->{category} ? (category => 'Cows') : (), $test->{detailed} ? (detailed_information => 'Highland ones') : (), } }); }; } + + subtest "check priority not set for category with no priorities" => sub { + $report->discard_changes; + $report->update({ category => 'Cows', response_priority_id => undef }); + $report->discard_changes; + is $report->response_priority, undef, 'response priority not set'; + $user->user_body_permissions->delete; + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_edit_category' }); + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_edit_priority' }); + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ + button => 'save', + with_fields => { + priority => $rp->id, + category => 'Sheep', + } + }); + + $report->discard_changes; + is $report->response_priority, undef, 'response priority not set'; + }; + + subtest "check can set priority for category when changing from category with no priorities" => sub { + $report->discard_changes; + $report->update({ category => 'Sheep', response_priority_id => undef }); + $report->discard_changes; + is $report->response_priority, undef, 'response priority not set'; + $user->user_body_permissions->delete; + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_edit_category' }); + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_edit_priority' }); + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ + button => 'save', + with_fields => { + priority => $rp->id, + category => 'Cows', + } + }); + + $report->discard_changes; + is $report->response_priority->id, $rp->id, 'response priority set'; + }; + + subtest "check can't set priority that isn't for a category" => sub { + $report->discard_changes; + $report->update({ category => 'Cows', response_priority_id => $rp->id }); + $report->discard_changes; + is $report->response_priority->id, $rp->id, 'response priority set'; + $user->user_body_permissions->delete; + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_edit_category' }); + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_edit_priority' }); + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ + button => 'save', + with_fields => { + priority => $rp2->id, + } + }); + + $report->discard_changes; + is $report->response_priority, undef, 'response priority set'; + }; + + subtest "check nearest address display" => sub { + $mech->get_ok("/report/$report_id"); + $mech->content_lacks('Nearest calculated address', 'No address displayed'); + + my $data = { + resourceSets => [ { + resources => [ { + address => { + addressLine => 'Constitution Hill', + locality => 'London', + } + } ], + } ], + }; + $report->geocode($data); + $report->update; + $mech->get_ok("/report/$report_id"); + $mech->content_lacks('Nearest calculated address', 'No address displayed'); + + $data = { + resourceSets => [ { + resources => [ { + name => 'Constitution Hill, London, SW1A', + address => { + addressLine => 'Constitution Hill', + locality => 'London', + } + } ], + } ], + }; + $report->geocode($data); + $report->update; + $mech->get_ok("/report/$report_id"); + $mech->content_contains('Nearest calculated address', 'Address displayed'); + $mech->content_contains('Constitution Hill, London, SW1A', 'Correct address displayed'); + } }; +foreach my $test ( + { cobrand => 'fixmystreet', limited => 0, desc => 'detailed_information has no max length' }, + { cobrand => 'oxfordshire', limited => 1, desc => 'detailed_information has max length' }, +) { + + FixMyStreet::override_config { + ALLOWED_COBRANDS => $test->{cobrand}, + }, sub { + subtest $test->{desc} => sub { + $user->user_body_permissions->delete; + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_inspect' }); + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ + button => 'save', + with_fields => { + include_update => 0, + detailed_information => 'XXX164XXX' . 'x' x (164-9) + } + }); + + $report->discard_changes; + like $report->get_extra_metadata('detailed_information'), qr/XXX164XXX/, 'detailed information saved'; + $mech->content_lacks('limited to 164 characters', "164 charcters of detailed information ok"); + $mech->content_contains('XXX164XXX', "Detailed information field contains submitted text"); + + $mech->submit_form_ok({ + button => 'save', + with_fields => { + include_update => 0, + detailed_information => 'XXX165XXX' . 'x' x (164-8) + } + }); + if ($test->{limited}) { + $mech->content_contains('164 characters maximum'); + $mech->content_contains('limited to 164 characters', "165 charcters of detailed information not ok"); + $mech->content_contains('XXX165XXX', "Detailed information field contains submitted text"); + + $report->discard_changes; + like $report->get_extra_metadata('detailed_information'), qr/XXX164XXX/, 'detailed information not saved'; + } else { + $mech->content_lacks(' characters maximum'); + $mech->content_lacks('limited to 164 characters', "165 charcters of detailed information ok"); + $mech->content_contains('XXX165XXX', "Detailed information field contains submitted text"); + + $report->discard_changes; + like $report->get_extra_metadata('detailed_information'), qr/XXX165XXX/, 'detailed information saved'; + } + }; + }; +} + FixMyStreet::override_config { ALLOWED_COBRANDS => 'oxfordshire', }, sub { @@ -320,6 +542,61 @@ FixMyStreet::override_config { }; FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => 'fixmystreet', +}, sub { + subtest "test category not updated if fail to include public update" => sub { + $mech->get_ok("/report/$report_id"); + $mech->submit_form(button => 'save', with_fields => { category => 'Badgers' }); + + $report->discard_changes; + is $report->category, "Cows", "Report in correct category"; + $mech->content_contains('Badgers" selected', 'Changed category still selected'); + }; + + subtest "test invalid form maintains Category and priority" => sub { + $mech->get_ok("/report/$report_id"); + my $expected_fields = { + state => 'action scheduled', + category => 'Cows', + public_update => '', + priority => $rp->id, + include_update => '1', + detailed_information => 'XXX164XXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + defect_type => '', + traffic_information => '' + }; + my $values = $mech->visible_form_values('report_inspect_form'); + is_deeply $values, $expected_fields, 'correct form fields present'; + + $mech->submit_form(button => 'save', with_fields => { category => 'Badgers', priority => $rp2->id }); + + $expected_fields->{category} = 'Badgers'; + $expected_fields->{priority} = $rp2->id; + + my $new_values = $mech->visible_form_values('report_inspect_form'); + is_deeply $new_values, $expected_fields, 'correct form fields present'; + }; + + subtest "test changing category and leaving an update only creates one comment" => sub { + $report->comments->delete; + $mech->get_ok("/report/$report_id"); + $mech->submit_form( + button => 'save', + with_fields => { + category => 'Badgers', + include_update => 1, + public_update => 'This is a public update', + }); + + $report->discard_changes; + is $report->category, "Badgers", "Report in correct category"; + is $report->comments->count, 1, "Only leaves one update"; + like $report->comments->first->text, qr/Category changed.*Badgers/, 'update text included category change'; + }; +}; + +FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'oxfordshire', 'fixmystreet' ], BASE_URL => 'http://fixmystreet.site', }, sub { diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t index ab6b5d78e..e0fe205bd 100644 --- a/t/app/controller/report_new.t +++ b/t/app/controller/report_new.t @@ -101,6 +101,7 @@ foreach my $test ( photo3 => '', name => '', may_show_name => '1', + username => '', email => '', phone => '', password_sign_in => '', @@ -127,6 +128,7 @@ foreach my $test ( photo3 => '', name => '', may_show_name => '1', + username => '', email => '', phone => '', category => 'Something bad', @@ -156,6 +158,7 @@ foreach my $test ( photo3 => '', name => '', may_show_name => '1', + username => '', email => '', phone => '', category => 'Street lighting', @@ -182,6 +185,7 @@ foreach my $test ( photo3 => '', name => '', may_show_name => undef, + username => '', email => '', phone => '', category => 'Street lighting', @@ -208,6 +212,7 @@ foreach my $test ( photo3 => '', name => 'Bob Jones', may_show_name => undef, + username => '', email => '', phone => '', category => 'Street lighting', @@ -233,6 +238,7 @@ foreach my $test ( photo3 => '', name => 'Bob Jones', may_show_name => '1', + username => '', email => '', phone => '', category => 'Street lighting', @@ -258,6 +264,7 @@ foreach my $test ( photo3 => '', name => 'Bob Jones', may_show_name => '1', + username => '', email => '', phone => '', category => 'Street lighting', @@ -283,6 +290,7 @@ foreach my $test ( photo3 => '', name => 'DUDE', may_show_name => '1', + username => '', email => '', phone => '', category => 'Street lighting', @@ -307,6 +315,7 @@ foreach my $test ( photo3 => '', name => 'anonymous', may_show_name => '1', + username => '', email => '', phone => '', category => 'Street lighting', @@ -331,14 +340,15 @@ foreach my $test ( photo3 => '', name => 'Joe Smith', may_show_name => '1', - email => 'not an email', + username => 'not an email', + email => '', phone => '', category => 'Street lighting', password_sign_in => '', password_register => '', remember_me => undef, }, - changes => { email => 'notanemail', }, + changes => { username => 'notanemail', email => 'notanemail' }, errors => [ 'Please enter a valid email', ], }, { @@ -352,6 +362,7 @@ foreach my $test ( photo3 => '', name => '', may_show_name => '1', + username => '', email => '', phone => '', category => 'Street lighting', @@ -379,7 +390,8 @@ foreach my $test ( photo3 => '', name => ' Bob Jones ', may_show_name => '1', - email => ' BOB @ExAmplE.COM ', + username => ' BOB @ExAmplE.COM ', + email => '', phone => '', category => 'Street lighting', password_sign_in => '', @@ -388,6 +400,7 @@ foreach my $test ( }, changes => { name => 'Bob Jones', + username => 'bob@example.com', email => 'bob@example.com', }, errors => [ 'Please enter a subject', 'Please enter some details', ], @@ -403,6 +416,7 @@ foreach my $test ( photo3 => '', name => 'Bob Jones', may_show_name => '1', + username => 'bob@example.com', email => 'bob@example.com', phone => '', category => 'Street lighting', @@ -426,6 +440,7 @@ foreach my $test ( photo3 => '', name => 'Bob Jones', may_show_name => '1', + username => 'bob@example.com', email => 'bob@example.com', phone => '', category => 'Street lighting', @@ -449,6 +464,7 @@ foreach my $test ( photo3 => '', name => 'Bob Jones', may_show_name => '1', + username => 'bob@example.com', email => 'bob@example.com', phone => '', category => 'Street lighting', @@ -560,7 +576,7 @@ foreach my $test ( photo1 => '', name => 'Joe Bloggs', may_show_name => '1', - email => 'test-1@example.com', + username => 'test-1@example.com', phone => '07903 123 456', category => 'Street lighting', password_register => $test->{password} ? 'secret' : '', @@ -674,7 +690,7 @@ subtest "test password errors for a user who is signing in as they report" => su title => 'Test Report', detail => 'Test report details.', photo1 => '', - email => 'test-2@example.com', + username => 'test-2@example.com', password_sign_in => 'secret1', category => 'Street lighting', } @@ -685,7 +701,7 @@ subtest "test password errors for a user who is signing in as they report" => su # check that we got the errors expected is_deeply $mech->page_errors, [ - "There was a problem with your email/password combination. If you cannot remember your password, or do not have one, please fill in the \x{2018}sign in by email\x{2019} section of the form.", + "There was a problem with your login information. If you cannot remember your password, or do not have one, please fill in the \x{2018}No\x{2019} section of the form.", ], "check there were errors"; }; @@ -726,7 +742,7 @@ subtest "test report creation for a user who is signing in as they report" => su title => 'Test Report', detail => 'Test report details.', photo1 => '', - email => 'test-2@example.com', + username => 'test-2@example.com', password_sign_in => 'secret2', category => 'Street lighting', } @@ -877,7 +893,7 @@ foreach my $test ( # Test that AJAX pages return the right data $mech->get_ok( - '/ajax?bbox=' . ($report->longitude - 0.01) . ',' . ($report->latitude - 0.01) + '/around?ajax=1&bbox=' . ($report->longitude - 0.01) . ',' . ($report->latitude - 0.01) . ',' . ($report->longitude + 0.01) . ',' . ($report->latitude + 0.01) ); $mech->content_contains( "Test Report at caf\xc3\xa9" ); @@ -947,7 +963,7 @@ subtest "test report creation for a category that is non public" => sub { title => 'Test Report', detail => 'Test report details.', photo1 => '', - email => 'test-2@example.com', + username => 'test-2@example.com', name => 'Joe Bloggs', category => 'Street lighting', } @@ -1040,6 +1056,51 @@ subtest "check that a lat/lon off coast leads to /around" => sub { }; +subtest "check we load a partial report correctly" => sub { + my $user = FixMyStreet::App->model('DB::User')->find_or_create( + { + email => 'test-partial@example.com' + } + ); + + my $report = FixMyStreet::App->model('DB::Problem')->create( { + name => '', + postcode => '', + category => 'Street lighting', + title => 'Testing', + detail => "Testing Detail", + anonymous => 0, + state => 'partial', + lang => 'en-gb', + service => '', + areas => '', + used_map => 1, + latitude => '51.754926', + longitude => '-1.256179', + user_id => $user->id, + } ); + + my $report_id = $report->id; + + my $token = FixMyStreet::App->model("DB::Token") + ->create( { scope => 'partial', data => $report->id } ); + + my $token_code = $token->token; + + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + }, + sub { + $mech->get("/L/$token_code"); + is $mech->res->previous->code, 302, 'partial token page redirects'; + is $mech->uri->path, "/report/new", "partial redirects to report page"; + $mech->content_contains('Testing Detail'); + }; + + $mech->delete_user($user); +}; + for my $test ( { desc => 'user title not set if not bromley problem', @@ -1135,7 +1196,7 @@ for my $test ( title => "Test Report", detail => 'Test report details.', photo1 => '', - email => 'firstlast@example.com', + username => 'firstlast@example.com', may_show_name => '1', phone => '07903 123 456', category => 'Trees', @@ -1167,9 +1228,7 @@ for my $test ( # confirm token in order to update the user details $mech->get_ok($url); - my $user = - FixMyStreet::App->model('DB::User') - ->find( { email => 'firstlast@example.com' } ); + my $user = FixMyStreet::App->model('DB::User')->find( { email => 'firstlast@example.com' } ); my $report = $user->problems->first; ok $report, "Found the report"; @@ -1284,7 +1343,7 @@ subtest "test Hart" => sub { $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' ); + ( username => $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 @@ -1424,7 +1483,7 @@ subtest "unresponsive body handling works" => sub { detail => 'Test report details.', photo1 => '', name => 'Joe Bloggs', - email => $test_email, + username => $test_email, may_show_name => '1', phone => '07903 123 456', category => 'Trees', @@ -1497,7 +1556,7 @@ subtest "unresponsive body handling works" => sub { detail => 'Test report details.', photo1 => '', name => 'Joe Bloggs', - email => $test_email, + username => $test_email, may_show_name => '1', phone => '07903 123 456', category => 'Trees', @@ -1618,7 +1677,7 @@ subtest "extra google analytics code displayed on email confirmation problem cre title => "Test Report", detail => 'Test report details.', photo1 => '', - email => 'firstlast@example.com', + username => 'firstlast@example.com', name => 'Test User', may_show_name => '1', phone => '07903 123 456', @@ -1639,9 +1698,7 @@ subtest "extra google analytics code displayed on email confirmation problem cre $mech->get_ok($url); # find the report - my $user = - FixMyStreet::App->model('DB::User') - ->find( { email => 'firstlast@example.com' } ); + my $user = FixMyStreet::App->model('DB::User')->find( { email => 'firstlast@example.com' } ); my $report = $user->problems->first; ok $report, "Found the report"; diff --git a/t/app/controller/report_new_open311.t b/t/app/controller/report_new_open311.t index 9a4a81182..0224e7e47 100644 --- a/t/app/controller/report_new_open311.t +++ b/t/app/controller/report_new_open311.t @@ -54,6 +54,7 @@ foreach my $test ( photo3 => '', name => '', may_show_name => '1', + username => '', email => '', phone => '', category => 'Street lighting', @@ -76,7 +77,7 @@ foreach my $test ( title => 'test', detail => 'test detail', name => 'Test User', - email => 'testopen311@example.com', + username => 'testopen311@example.com', category => 'Street lighting', number => 27, }, @@ -100,7 +101,7 @@ foreach my $test ( $mech->clear_emails_ok; # check that the user does not exist - my $test_email = $test->{submit_with}->{email}; + my $test_email = $test->{submit_with}->{username}; my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } ); if ( $user ) { $user->problems->delete; diff --git a/t/app/controller/report_new_text.t b/t/app/controller/report_new_text.t new file mode 100644 index 000000000..734b9dbb4 --- /dev/null +++ b/t/app/controller/report_new_text.t @@ -0,0 +1,371 @@ +use FixMyStreet::TestMech; +use t::Mock::Twilio; + +my $twilio = t::Mock::Twilio->new; +LWP::Protocol::PSGI->register($twilio->to_psgi_app, host => 'api.twilio.com'); + +# 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(2651, 'City of Edinburgh Council'); +$mech->create_contact_ok( body_id => $body->id, category => 'Street lighting', email => 'highways@example.com' ); +$mech->create_contact_ok( body_id => $body->id, category => 'Trees', email => 'trees@example.com' ); + +# test that phone number validation works okay +foreach my $test ( + { + msg => 'invalid number', + pc => 'EH1 1BB', + fields => { + username => '0121 4960000000', email => '', phone => '', + title => 'Title', detail => 'Detail', name => 'Bob Jones', + category => 'Street lighting', + may_show_name => '1', remember_me => undef, + photo1 => '', photo2 => '', photo3 => '', + password_register => '', password_sign_in => '', + }, + changes => { + username => '01214960000000', + phone => '01214960000000', + }, + errors => [ 'Please check your phone number is correct' ], + }, + { + msg => 'landline number', + pc => 'EH1 1BB', + fields => { + username => '0121 4960000', email => '', phone => '', + title => 'Title', detail => 'Detail', name => 'Bob Jones', + category => 'Street lighting', + may_show_name => '1', remember_me => undef, + photo1 => '', photo2 => '', photo3 => '', + password_register => '', password_sign_in => '', + }, + changes => { + username => '+44 121 496 0000', + phone => '+44 121 496 0000', + }, + errors => [ 'Please enter a mobile number', ], + }, + ) +{ + subtest "check form errors where $test->{msg}" => sub { + $mech->get_ok('/around'); + + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + SMS_AUTHENTICATION => 1, + PHONE_COUNTRY => 'GB', + }, 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 [ sort @{$mech->page_errors} ], [ sort @{$test->{errors}} ], "check errors"; + + # check that fields have changed as expected + my $new_values = { + %{ $test->{fields} }, # values added to form + %{ $test->{changes} }, # changes we expect + }; + is_deeply $mech->visible_form_values, $new_values, + "values correctly changed"; + }; +} + +my $test_phone = '+61491570156'; +my $first_user; +foreach my $test ( + { + desc => 'does not have an account, does not set a password', + user => 0, password => 0, + }, + { + desc => 'does not have an account, sets a password', + user => 0, password => 1, + }, + { + desc => 'does have an account and is not signed in; does not sign in, does not set a password', + user => 1, password => 0, + }, + { + desc => 'does have an account and is not signed in; does not sign in, sets a password', + user => 1, password => 1, + }, +) { + subtest "test report creation for a user who " . $test->{desc} => sub { + $mech->log_out_ok; + + if ($test->{user}) { + my $user = FixMyStreet::App->model('DB::User')->find( { phone => $test_phone } ); + ok $user, "test user does exist"; + $user->problems->delete; + $user->name( 'Old Name' ); + $user->password( 'old_password' ); + $user->update; + } elsif (!$first_user) { + ok !FixMyStreet::App->model('DB::User')->find( { phone => $test_phone } ), + "test user does not exist"; + $first_user = 1; + } else { + # Not first pass, so will exist, but want no user to start, so delete it. + $mech->delete_user($test_phone); + } + + $mech->get_ok('/around'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + SMS_AUTHENTICATION => 1, + PHONE_COUNTRY => 'GB', + TWILIO_ACCOUNT_SID => 'AC123', + }, 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" ); + $mech->submit_form_ok( + { + button => 'submit_register', + with_fields => { + title => 'Test Report', detail => 'Test report details.', + photo1 => '', + name => 'Joe Bloggs', may_show_name => '1', + username => $test_phone, + category => 'Street lighting', + password_register => $test->{password} ? 'secret' : '', + } + }, + "submit good details" + ); + }; + + is_deeply $mech->page_errors, [], "check there were no errors"; + + my $user = FixMyStreet::App->model('DB::User')->find( { phone => $test_phone } ); + ok $user, "user found"; + if ($test->{user}) { + is $user->name, 'Old Name', 'name unchanged'; + ok $user->check_password('old_password'), 'password unchanged'; + } else { + is $user->name, undef, 'name not yet set'; + is $user->password, '', 'password not yet set for new user'; + } + + my $report = $user->problems->first; + ok $report, "Found the report"; + is $report->state, 'unconfirmed', "report not confirmed"; + is $report->bodies_str, $body->id; + + $mech->submit_form_ok({ with_fields => { code => '00000' } }); + $mech->content_contains('Try again'); + + my $code = $twilio->get_text_code; + $mech->submit_form_ok({ with_fields => { code => $code } }); + + $report->discard_changes; + is $report->state, 'confirmed', "Report is now confirmed"; + + $mech->get_ok( '/report/' . $report->id ); + + is $report->name, 'Joe Bloggs', 'name updated correctly'; + if ($test->{password}) { + ok $report->user->check_password('secret'), 'password updated correctly'; + } elsif ($test->{user}) { + ok $report->user->check_password('old_password'), 'password unchanged, as no new one given'; + } else { + is $report->user->password, '', 'password still not set, as none given'; + } + + # check that the reporter has an alert + my $alert = FixMyStreet::App->model('DB::Alert')->find( { + user => $report->user, + alert_type => 'new_updates', + parameter => $report->id, + } ); + ok $alert, "created new alert"; + + # user is created and logged in + $mech->logged_in_ok; + + # cleanup + $mech->delete_user($user) + if $test->{user} && $test->{password}; + }; +} + +# this test to make sure that we don't see spurious error messages about +# the name being blank when there is a sign in error +subtest "test password errors for a user who is signing in as they report" => sub { + $mech->log_out_ok; + + my $user = $mech->create_user_ok($test_phone); + ok $user->update( { + name => 'Joe Bloggs', + email => 'joe@example.net', + password => 'secret2', + } ), "set user details"; + + $mech->get_ok('/around'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + SMS_AUTHENTICATION => 1, + }, 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" ); + $mech->submit_form_ok( + { + button => 'submit_sign_in', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + photo1 => '', + username => $test_phone, + password_sign_in => 'secret1', + category => 'Street lighting', + } + }, + "submit with wrong password" + ); + }; + + # check that we got the errors expected + is_deeply $mech->page_errors, [ + "There was a problem with your login information. If you cannot remember your password, or do not have one, please fill in the \x{2018}No\x{2019} section of the form.", + ], "check there were errors"; +}; + +subtest "test report creation for a user who is signing in as they report" => sub { + $mech->log_out_ok; + $mech->cookie_jar({}); + + my $user = $mech->create_user_ok($test_phone); + + $mech->get_ok('/around'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + SMS_AUTHENTICATION => 1, + }, 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" ); + $mech->submit_form_ok( + { + button => 'submit_sign_in', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + photo1 => '', + username => $test_phone, + password_sign_in => 'secret2', + category => 'Street lighting', + } + }, + "submit good details" + ); + + # 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" + ); + }; + + my $report = $user->problems->first; + ok $report, "Found the report"; + $mech->content_contains('Thank you for reporting this issue'); + is $report->bodies_str, $body->id; + is $report->state, 'confirmed', "report is now confirmed"; + $mech->get_ok( '/report/' . $report->id ); + my $alert = FixMyStreet::App->model('DB::Alert')->find( { + user => $report->user, + alert_type => 'new_updates', + parameter => $report->id, + } ); + ok $alert, "created new alert"; + + $mech->logged_in_ok; +}; + +subtest "test report creation for a user who is logged in" => sub { + my $user = $mech->create_user_ok($test_phone); + $mech->get_ok('/around'); + 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" ); + is_deeply( + $mech->visible_form_values, + { + title => '', + detail => '', + may_show_name => '1', + name => 'Joe Bloggs', + email => 'joe@example.net', + 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', + category => 'Street lighting', + } + }, + "submit good details" + ); + }; + + my $report = $user->problems->first; + ok $report, "Found the report"; + is $report->bodies_str, $body->id; + $mech->content_contains('Thank you for reporting this issue'); + is $report->state, 'confirmed', "report is now confirmed"; + $mech->get_ok( '/report/' . $report->id ); + my $alert = FixMyStreet::App->model('DB::Alert')->find( { + user => $report->user, + alert_type => 'new_updates', + parameter => $report->id, + } ); + ok $alert, "created new alert"; + + $mech->logged_in_ok; + + $mech->get_ok( + '/around?ajax=1&bbox=' . ($report->longitude - 0.01) . ',' . ($report->latitude - 0.01) + . ',' . ($report->longitude + 0.01) . ',' . ($report->latitude + 0.01) + ); + $mech->content_contains( "Test Report at caf\xc3\xa9" ); +}; + +done_testing(); diff --git a/t/app/controller/report_update_text.t b/t/app/controller/report_update_text.t new file mode 100644 index 000000000..45b4e78c2 --- /dev/null +++ b/t/app/controller/report_update_text.t @@ -0,0 +1,307 @@ +use FixMyStreet::TestMech; +use t::Mock::Twilio; + +my $twilio = t::Mock::Twilio->new; +LWP::Protocol::PSGI->register($twilio->to_psgi_app, host => 'api.twilio.com'); + +my $mech = FixMyStreet::TestMech->new; +my $user = $mech->create_user_ok('test@example.com', name => 'Test 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, + month => 04, + day => 16, + hour => 15, + minute => 47, + second => 23 +); + +my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( + { + postcode => 'SW1A 1AA', + bodies_str => $body->id, + 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 $report_id = $report->id; +ok $report, "created test report - $report_id"; + +my $comment = FixMyStreet::App->model('DB::Comment')->find_or_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->ymd . ' ' . $dt->hms, + anonymous => 'f', +}); + +my $comment_id = $comment->id; +ok $comment, "created test update - $comment_id"; + +for my $test ( + { + desc => 'Invalid phone', + fields => { + username => '01214960000000', + update => 'Update', + name => 'Name', + photo1 => '', + photo2 => '', + photo3 => '', + fixed => undef, + add_alert => 1, + may_show_name => undef, + remember_me => undef, + password_sign_in => '', + password_register => '', + }, + changes => {}, + field_errors => [ 'Please check your phone number is correct' ] + }, + { + desc => 'landline number', + fields => { + username => '01214960000', + update => 'Update', + name => 'Name', + photo1 => '', + photo2 => '', + photo3 => '', + fixed => undef, + add_alert => 1, + may_show_name => undef, + remember_me => undef, + password_register => '', + password_sign_in => '', + }, + changes => { + username => '+44 121 496 0000', + }, + field_errors => [ 'Please enter a mobile number' ] + }, + ) +{ + subtest "submit an update - $test->{desc}" => sub { + $mech->get_ok("/report/$report_id"); + + FixMyStreet::override_config { + SMS_AUTHENTICATION => 1, + PHONE_COUNTRY => 'GB', + }, sub { + $mech->submit_form_ok( { with_fields => $test->{fields} }, 'submit update' ); + }; + + is_deeply $mech->page_errors, $test->{field_errors}, 'field errors'; + + my $values = { + %{ $test->{fields} }, + %{ $test->{changes} }, + }; + + is_deeply $mech->visible_form_values('updateForm'), $values, 'form changes'; + }; +} + +my $test_phone = '+61491570156'; +for my $test ( + { + desc => 'submit an update, unregistered, logged out', + form_values => { + submit_update => 1, + username => $test_phone, + update => 'Update from an unregistered user', + add_alert => undef, + name => 'Unreg User', + may_show_name => undef, + }, + }, + { + desc => 'submit an update, unregistered, logged out, sign up for alerts', + form_values => { + submit_update => 1, + username => $test_phone, + update => 'Update from an unregistered user', + add_alert => 1, + name => 'Unreg User', + may_show_name => undef, + }, + }, + { + desc => 'submit an update, registered, logged out, confirming by text', + registered => 1, + form_values => { + submit_update => 1, + username => $test_phone, + update => 'Update from a registered user', + add_alert => undef, + name => 'Reg User', + password_register => 'new_secret', + }, + }, +) { + subtest $test->{desc} => sub { + $mech->log_out_ok(); + my $user; + if ($test->{registered}) { + $user = $mech->create_user_ok( $test_phone ); + $user->update( { name => 'Mr Reg', password => 'secret2' } ); + } + + $mech->get_ok("/report/$report_id"); + FixMyStreet::override_config { + SMS_AUTHENTICATION => 1, + TWILIO_ACCOUNT_SID => 'AC123', + }, sub { + $mech->submit_form_ok( { with_fields => $test->{form_values} }, 'submit update'); + }; + $mech->content_contains('Nearly done! Now check your phone'); + + if ($user) { + $user->discard_changes; + ok $user->check_password( 'secret2' ), 'password unchanged'; + is $user->name, 'Mr Reg', 'name unchanged'; + } + + my ($token) = $mech->content =~ /name="token" value="([^"]*)"/; + $token = FixMyStreet::App->model('DB::Token')->find({ + token => $token, + scope => 'comment' + }); + ok $token, 'Token found in database'; + + my $update_id = $token->data->{id}; + my $add_alerts = $token->data->{add_alert}; + my $update = FixMyStreet::App->model('DB::Comment')->find( { id => $update_id } ); + + ok $update, 'found update in database'; + is $update->state, 'unconfirmed', 'update unconfirmed'; + my $details = $test->{form_values}; + is $update->user->phone, $details->{username}, 'update phone'; + is $update->user->phone_verified, 1; + is $update->text, $details->{update}, 'update text'; + is $add_alerts, $details->{add_alert} ? 1 : 0, 'do not sign up for alerts'; + + my $code = $twilio->get_text_code; + $mech->submit_form_ok( { with_fields => { code => '00000' } }); + $mech->content_contains('Try again'); + $mech->submit_form_ok( { with_fields => { code => $code } }); + + $mech->content_contains("/report/$report_id#update_$update_id"); + + if ($user) { + $user->discard_changes; + ok $user->check_password( 'new_secret' ), 'password changed'; + is $user->name, 'Reg User', 'name changed'; + } else { + $user = FixMyStreet::App->model( 'DB::User' )->find( { phone => $details->{username} } ); + ok $user, 'found user'; + } + + my $alert = FixMyStreet::App->model( 'DB::Alert' )->find( + { user => $user, alert_type => 'new_updates', confirmed => 1, } + ); + + ok $details->{add_alert} ? defined( $alert ) : !defined( $alert ), 'sign up for alerts'; + + $update->discard_changes; + is $update->state, 'confirmed', 'update confirmed'; + $mech->delete_user( $user ); + }; +} + +for my $test ( + { + desc => 'submit an update for a registered user, signing in with wrong password', + form_values => { + submit_update => 1, + username => $test_phone, + update => 'Update from a user', + add_alert => undef, + password_sign_in => 'secret', + }, + field_errors => [ + "There was a problem with your login information. If you cannot remember your password, or do not have one, please fill in the \x{2018}No\x{2019} section of the form.", + 'Please enter your name', # FIXME Not really necessary error + ], + }, + { + desc => 'submit an update for a registered user and sign in', + form_values => { + submit_update => 1, + username => $test_phone, + update => 'Update from a user', + add_alert => undef, + password_sign_in => 'secret2', + }, + message => 'You have successfully signed in; please check and confirm your details are accurate:', + } +) { + subtest $test->{desc} => sub { + # Set things up + my $user = $mech->create_user_ok( $test->{form_values}->{username} ); + my $pw = 'secret2'; + $user->update( { name => 'Mr Reg', password => $pw } ); + $report->comments->delete; + + $mech->log_out_ok(); + $mech->clear_emails_ok(); + $mech->get_ok("/report/$report_id"); + FixMyStreet::override_config { + SMS_AUTHENTICATION => 1, + }, sub { + $mech->submit_form_ok( + { + button => 'submit_sign_in', + with_fields => $test->{form_values} + }, + 'submit update' + ); + }; + + $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", 4 ) unless $test->{form_values}{password_sign_in} eq $pw; + + # Now submit with a name + $mech->submit_form_ok( + { with_fields => { name => 'Joe Bloggs', } }, + "submit good details" + ); + + $mech->content_contains('Thank you for updating this issue'); + + my $update = $report->comments->first; + ok $update, 'found update'; + is $update->text, $test->{form_values}->{update}, 'update text'; + is $update->user->phone, $test->{form_values}->{username}, 'update user'; + is $update->state, 'confirmed', 'update confirmed'; + $mech->delete_user( $update->user ); + } + }; +} + +done_testing(); diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t index 0526b2fd7..aefe77e47 100644 --- a/t/app/controller/report_updates.t +++ b/t/app/controller/report_updates.t @@ -196,7 +196,7 @@ for my $test ( { desc => 'No email, no message', fields => { - rznvy => '', + username => '', update => '', name => '', photo1 => '', @@ -215,7 +215,7 @@ for my $test ( { desc => 'Invalid email, no message', fields => { - rznvy => 'test', + username => 'test', update => '', name => '', photo1 => '', @@ -234,7 +234,7 @@ for my $test ( { desc => 'email with spaces, no message', fields => { - rznvy => 'test @ example. com', + username => 'test @ example. com', update => '', name => '', photo1 => '', @@ -248,14 +248,14 @@ for my $test ( password_sign_in => '', }, changes => { - rznvy => 'test@example.com', + username => 'test@example.com', }, field_errors => [ 'Please enter a message', 'Please enter your name' ] }, { desc => 'email with uppercase, no message', fields => { - rznvy => 'test@EXAMPLE.COM', + username => 'test@EXAMPLE.COM', update => '', name => '', photo1 => '', @@ -269,7 +269,7 @@ for my $test ( password_sign_in => '', }, changes => { - rznvy => 'test@example.com', + username => 'test@example.com', }, field_errors => [ 'Please enter a message', 'Please enter your name' ] }, @@ -297,7 +297,7 @@ for my $test ( desc => 'submit an update for a non registered user', initial_values => { name => '', - rznvy => '', + username => '', may_show_name => 1, add_alert => 1, photo1 => '', @@ -311,7 +311,7 @@ for my $test ( }, form_values => { submit_update => 1, - rznvy => 'unregistered@example.com', + username => 'unregistered@example.com', update => 'Update from an unregistered user', add_alert => undef, name => 'Unreg User', @@ -323,7 +323,7 @@ for my $test ( desc => 'submit an update for a non registered user and sign up', initial_values => { name => '', - rznvy => '', + username => '', may_show_name => 1, add_alert => 1, photo1 => '', @@ -337,7 +337,7 @@ for my $test ( }, form_values => { submit_update => 1, - rznvy => 'unregistered@example.com', + username => 'unregistered@example.com', update => "update from an\r\n\r\nunregistered user", add_alert => 1, name => 'Unreg User', @@ -395,14 +395,14 @@ for my $test ( ok $update, 'found update in database'; is $update->state, 'unconfirmed', 'update unconfirmed'; - is $update->user->email, $details->{rznvy}, 'update email'; + is $update->user->email, $details->{username}, 'update email'; 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 ); $mech->content_contains("/report/$report_id#update_$update_id"); - my $unreg_user = FixMyStreet::App->model( 'DB::User' )->find( { email => $details->{rznvy} } ); + my $unreg_user = FixMyStreet::App->model( 'DB::User' )->find( { email => $details->{username} } ); ok $unreg_user, 'found user'; @@ -427,7 +427,7 @@ for my $test ( desc => 'overriding email confirmation allows report confirmation with no email sent', initial_values => { name => '', - rznvy => '', + username => '', may_show_name => 1, add_alert => 1, photo1 => '', @@ -441,7 +441,7 @@ for my $test ( }, form_values => { submit_update => 1, - rznvy => 'unregistered@example.com', + username => 'unregistered@example.com', update => "update no email confirm", add_alert => 1, name => 'Unreg User', @@ -493,10 +493,10 @@ for my $test ( ok $update, 'found update in database'; is $update->state, 'confirmed', 'update confirmed'; - is $update->user->email, $details->{rznvy}, 'update email'; + is $update->user->email, $details->{username}, 'update email'; is $update->text, $details->{update}, 'update text'; - my $unreg_user = FixMyStreet::App->model( 'DB::User' )->find( { email => $details->{rznvy} } ); + my $unreg_user = FixMyStreet::App->model( 'DB::User' )->find( { email => $details->{username} } ); ok $unreg_user, 'found user'; @@ -918,16 +918,96 @@ subtest 'check meta correct for second comment marking as reopened' => sub { like $update_meta->[2], qr/Open/, 'update meta says reopened'; }; -subtest "check first comment with status change but no text is displayed" => sub { - $user->from_body( $body->id ); - $user->update; +subtest 'check meta correct for comment after mark_fixed with not problem_state' => 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 => '', + 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/i, 'update meta says fixed'; + + $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 => 'fixed - user', + anonymous => 0, + mark_open => 0, + mark_fixed => 0, + state => 'confirmed', + } + ); + + $mech->get_ok( "/report/" . $report->id ); + $update_meta = $mech->extract_update_metas; + unlike $update_meta->[2], qr/Fixed/i, 'update meta does not say fixed'; +}; + +for my $test( + { + user => $user2, + name => $body->name, + body => $body, + superuser => 0, + desc =>"check first comment from body user with status change but no text is displayed" + }, + { + user => $user2, + name => $body->name, + superuser => 0, + bodyuser => 1, + desc =>"check first comment from ex body user with status change but no text is displayed" + }, + { + user => $user2, + name => $body->name, + body => $body, + superuser => 1, + desc =>"check first comment from body super user with status change but no text is displayed" + }, + { + user => $user2, + name => 'an administrator', + superuser => 1, + desc =>"check first comment from super user with status change but no text is displayed" + } +) { +subtest $test->{desc} => sub { + my $extra = {}; + if ($test->{body}) { + $extra->{is_body_user} = $test->{body}->id; + $user2->from_body( $test->{body}->id ); + } else { + if ($test->{superuser}) { + $extra->{is_superuser} = 1; + } elsif ($test->{bodyuser}) { + $extra->{is_body_user} = $body->id; + } + $user2->from_body(undef); + } + $user2->is_superuser($test->{superuser}); + $user2->update; $report->comments->delete; my $comment = FixMyStreet::App->model('DB::Comment')->create( { - user => $user, - name => $user->from_body->name, + user => $test->{user}, + name => $test->{name}, problem_id => $report->id, text => '', confirmed => DateTime->now( time_zone => 'local'), @@ -936,17 +1016,30 @@ subtest "check first comment with status change but no text is displayed" => sub mark_open => 0, mark_fixed => 0, state => 'confirmed', + extra => $extra, } ); $mech->log_in_ok( $user->email ); + + ok $user->user_body_permissions->search({ + body_id => $body->id, + permission_type => 'view_body_contribute_details' + })->delete, 'Remove user view_body_contribute_details permissions'; + $mech->get_ok("/report/$report_id"); my $update_meta = $mech->extract_update_metas; like $update_meta->[1], qr/Updated by/, 'updated by meta if no text'; - unlike $update_meta->[1], qr/Test User/, 'commenter name not included'; + unlike $update_meta->[1], qr/Commenter/, 'commenter name not included'; like $update_meta->[0], qr/investigating/i, 'update meta includes state change'; + if ($test->{body} || $test->{bodyuser}) { + like $update_meta->[1], qr/Westminster/, 'body user update uses body name'; + } elsif ($test->{superuser}) { + like $update_meta->[1], qr/an administrator/, 'superuser update says an administrator'; + } + ok $user->user_body_permissions->create({ body => $body, permission_type => 'view_body_contribute_details' @@ -955,10 +1048,108 @@ subtest "check first comment with status change but no text is displayed" => sub $mech->get_ok("/report/$report_id"); $update_meta = $mech->extract_update_metas; like $update_meta->[1], qr/Updated by/, 'updated by meta if no text'; - like $update_meta->[1], qr/Test User/, 'commenter name included if user has view contribute permission'; + like $update_meta->[1], qr/Commenter/, 'commenter name included if user has view contribute permission'; like $update_meta->[0], qr/investigating/i, 'update meta includes state change'; }; +} + +for my $test( + { + desc =>"check comment from super user hiding report is not displayed", + problem_state => 'hidden', + }, + { + desc =>"check comment from super user unconfirming report is not displayed", + problem_state => 'unconfirmed', + } +) { +subtest $test->{desc} => sub { + my $extra = { is_superuser => 1 }; + $user2->is_superuser(1); + $user2->update; + + $report->comments->delete; + + my $comment = FixMyStreet::App->model('DB::Comment')->create( + { + user => $user2, + name => 'an administrator', + problem_id => $report->id, + text => '', + confirmed => DateTime->now( time_zone => 'local'), + problem_state => $test->{problem_state}, + anonymous => 0, + mark_open => 0, + mark_fixed => 0, + state => 'confirmed', + extra => $extra, + } + ); + $mech->log_in_ok( $user->email ); + $mech->get_ok("/report/$report_id"); + + my $update_meta = $mech->extract_update_metas; + is scalar(@$update_meta), 0, 'no comments on report'; + }; +} + +for my $test( + { + desc =>"check comments from super user hiding and unhiding report are not displayed", + problem_states => [qw/hidden confirmed/], + comment_count => 0, + }, + { + desc =>"check comment from super user unconfirming and confirming report are is not displayed", + problem_states => [qw/unconfirmed confirmed/], + comment_count => 0, + }, + { + desc =>"check comment after unconfirming and confirming a report is displayed", + problem_states => [qw/unconfirmed confirmed investigating/], + comment_count => 2, # state change line + who updated line + }, + { + desc =>"check comment after confirming a report after blank state is not displayed", + problem_states => ['unconfirmed', '', 'confirmed'], + comment_count => 0, # state change line + who updated line + }, +) { +subtest $test->{desc} => sub { + my $extra = { is_superuser => 1 }; + $user2->is_superuser(1); + $user2->update; + + $report->comments->delete; + + for my $state (@{$test->{problem_states}}) { + my $comment = FixMyStreet::App->model('DB::Comment')->create( + { + user => $user2, + name => 'an administrator', + problem_id => $report->id, + text => '', + confirmed => DateTime->now( time_zone => 'local'), + problem_state => $state, + anonymous => 0, + mark_open => 0, + mark_fixed => 0, + state => 'confirmed', + extra => $extra, + } + ); + } + $mech->log_in_ok( $user->email ); + $mech->get_ok("/report/$report_id"); + + my $update_meta = $mech->extract_update_metas; + is scalar(@$update_meta), $test->{comment_count}, 'expected number of comments on report'; + }; +} +$user2->is_superuser(0); +$user2->from_body(undef); +$user2->update; $user->from_body(undef); $user->update; @@ -966,19 +1157,20 @@ $user->update; $report->state('confirmed'); $report->bodies_str($body->id); $report->update; +$report->comments->delete; for my $test ( { desc => 'submit an update for a registered user, signing in with wrong password', form_values => { submit_update => 1, - rznvy => 'registered@example.com', + username => 'registered@example.com', update => 'Update from a user', add_alert => undef, password_sign_in => 'secret', }, field_errors => [ - "There was a problem with your email/password combination. If you cannot remember your password, or do not have one, please fill in the \x{2018}sign in by email\x{2019} section of the form.", + "There was a problem with your login information. If you cannot remember your password, or do not have one, please fill in the \x{2018}No\x{2019} section of the form.", 'Please enter your name', # FIXME Not really necessary error ], }, @@ -986,7 +1178,7 @@ for my $test ( desc => 'submit an update for a registered user and sign in', form_values => { submit_update => 1, - rznvy => 'registered@example.com', + username => 'registered@example.com', update => 'Update from a user', add_alert => undef, password_sign_in => 'secret2', @@ -996,7 +1188,7 @@ for my $test ( ) { subtest $test->{desc} => sub { # Set things up - my $user = $mech->create_user_ok( $test->{form_values}->{rznvy} ); + my $user = $mech->create_user_ok( $test->{form_values}->{username} ); my $pw = 'secret2'; $user->update( { name => 'Mr Reg', password => $pw } ); $report->comments->delete; @@ -1036,7 +1228,7 @@ for my $test ( my $update = $report->comments->first; ok $update, 'found update'; is $update->text, $test->{form_values}->{update}, 'update text'; - is $update->user->email, $test->{form_values}->{rznvy}, 'update user'; + is $update->user->email, $test->{form_values}->{username}, 'update user'; is $update->state, 'confirmed', 'update confirmed'; $mech->delete_user( $update->user ); } @@ -1053,7 +1245,7 @@ subtest 'submit an update for a registered user, creating update by email' => su $mech->submit_form_ok( { with_fields => { submit_update => 1, - rznvy => 'registered@example.com', + username => 'registered@example.com', update => 'Update from a user', add_alert => undef, name => 'New Name', @@ -1502,7 +1694,7 @@ for my $test ( fields => { submit_update => 1, name => 'Test User', - rznvy => 'test@example.com', + username => 'test@example.com', may_show_name => 1, update => 'update from owner', add_alert => undef, @@ -1524,7 +1716,7 @@ for my $test ( submit_update => 1, name => 'Test User', may_show_name => 1, - rznvy => 'test@example.com', + username => 'test@example.com', update => 'update from owner', add_alert => undef, fixed => 1, @@ -1589,7 +1781,7 @@ for my $test ( my $update = $report->comments->first; ok $update, 'found update'; is $update->text, $results->{update}, 'update text'; - is $update->user->email, $test->{fields}->{rznvy}, 'update user'; + is $update->user->email, $test->{fields}->{username}, 'update user'; is $update->state, 'unconfirmed', 'update confirmed'; is $update->anonymous, $test->{anonymous}, 'user anonymous'; diff --git a/t/app/controller/reports.t b/t/app/controller/reports.t index f3958a0a5..76c920562 100644 --- a/t/app/controller/reports.t +++ b/t/app/controller/reports.t @@ -12,9 +12,6 @@ END { ok( my $mech = FixMyStreet::TestMech->new, 'Created mech object' ); -# Run the cron script with empty database -FixMyStreet::Script::UpdateAllReports::generate_dashboard(); - $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; @@ -99,10 +96,14 @@ $fife_problems[10]->update( { }); # Run the cron script that makes the data for /reports so we don't get an error. -FixMyStreet::Script::UpdateAllReports::generate_dashboard(); +my $data = FixMyStreet::Script::UpdateAllReports::generate_dashboard(); # check that we can get the page -$mech->get_ok('/reports'); +FixMyStreet::override_config { + TEST_DASHBOARD_DATA => $data, +}, sub { + $mech->get_ok('/reports'); +}; $mech->title_like(qr{Dashboard}); $mech->content_contains('Birmingham'); @@ -114,6 +115,18 @@ FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', }, sub { $mech->submit_form_ok( { with_fields => { body => $body_edin_id } }, 'Submitted dropdown okay' ); + is $mech->uri->path, '/reports/City+of+Edinburgh+Council'; + + subtest "test ward pages" => sub { + $mech->get_ok('/reports/Birmingham/Bad-Ward'); + is $mech->uri->path, '/reports/Birmingham+City+Council'; + $mech->get_ok('/reports/Birmingham/Aston'); + is $mech->uri->path, '/reports/Birmingham+City+Council/Aston'; + $mech->get_ok('/reports/Birmingham/Aston|Bournville'); + is $mech->uri->path, '/reports/Birmingham+City+Council/Aston%7CBournville'; + $mech->content_contains('Aston, Bournville'); + }; + $mech->get_ok('/reports/Westminster'); }; @@ -126,6 +139,7 @@ is scalar @$problems, 5, 'correct number of problems displayed'; FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', + TEST_DASHBOARD_DATA => $data, }, sub { $mech->get_ok('/reports'); $mech->submit_form_ok({ with_fields => { body => $body_slash_id } }, 'Submitted dropdown okay'); @@ -187,13 +201,18 @@ is scalar @$problems, 4, 'only public problems are displayed'; $mech->content_lacks('All reports Test 3 for ' . $body_west_id, 'non public problem is not visible'); # No change to numbers if report is non-public -$mech->get_ok('/reports'); +FixMyStreet::override_config { + TEST_DASHBOARD_DATA => $data, +}, sub { + $mech->get_ok('/reports'); +}; $mech->content_contains('"Apr","May","Jun","Jul"'); $mech->content_contains('5,9,10,22'); subtest "test fiksgatami all reports page" => sub { FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'fiksgatami' ], + TEST_DASHBOARD_DATA => $data, # Not relevant to what we're testing, just so page loads }, sub { $mech->create_body_ok(3, 'Oslo'); ok $mech->host("fiksgatami.no"), 'change host to fiksgatami'; diff --git a/t/app/controller/rss.t b/t/app/controller/rss.t index 5ec7bfae7..171121eaa 100644 --- a/t/app/controller/rss.t +++ b/t/app/controller/rss.t @@ -1,3 +1,4 @@ +use open ':std', ':locale'; use FixMyStreet::TestMech; use FixMyStreet::App; @@ -18,7 +19,7 @@ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( { bodies_str => '2651', areas => ',11808,135007,14419,134935,2651,20728,', category => 'Street lighting', - title => 'Testing', + title => '&Test’i<n>g \'☃"', detail => 'Testing Detail', used_map => 1, name => $user1->name, @@ -44,7 +45,7 @@ FixMyStreet::override_config { }, sub { $mech->get_ok("/rss/pc/EH11BB/2"); }; -$mech->content_contains( "Testing, 10th October" ); +$mech->content_contains( "&Test’i<n>g '☃", 10th October" ); $mech->content_lacks( 'Nearest road to the pin' ); is $mech->response->header('Access-Control-Allow-Origin'), '*'; @@ -118,7 +119,7 @@ FixMyStreet::override_config { }, sub { $mech->get_ok("/rss/pc/EH11BB/2"); }; -$mech->content_contains( "Testing, 10th October" ); +$mech->content_contains( "&Test’i<n>g '☃", 10th October" ); $mech->content_contains( '18 North Bridge, Edinburgh' ); $report->delete(); diff --git a/t/app/model/extra.t b/t/app/model/extra.t index a5e3e3574..55accb086 100644 --- a/t/app/model/extra.t +++ b/t/app/model/extra.t @@ -101,6 +101,7 @@ subtest 'Default hash layout' => sub { subtest 'Get named field values' => sub { my $user = $db->resultset('User')->create({ email => 'test-moderation@example.com', + email_verified => 1, name => 'Test User' }); my $report = $db->resultset('Problem')->create( diff --git a/t/app/model/photoset.t b/t/app/model/photoset.t index 4aa5c8992..d171ba88b 100644 --- a/t/app/model/photoset.t +++ b/t/app/model/photoset.t @@ -12,9 +12,7 @@ my $UPLOAD_DIR = tempdir( CLEANUP => 1 ); my $db = FixMyStreet::DB->schema; -my $user = $db->resultset('User')->find_or_create({ - name => 'Bob', email => 'bob@example.com', -}); +my $user = $db->resultset('User')->find_or_create({ name => 'Bob', email => 'bob@example.com' }); FixMyStreet::override_config { UPLOAD_DIR => $UPLOAD_DIR, |