diff options
Diffstat (limited to 't/app/controller/admin')
-rw-r--r-- | t/app/controller/admin/bodies.t | 165 | ||||
-rw-r--r-- | t/app/controller/admin/defecttypes.t | 193 | ||||
-rw-r--r-- | t/app/controller/admin/permissions.t | 196 | ||||
-rw-r--r-- | t/app/controller/admin/priorities.t | 102 | ||||
-rw-r--r-- | t/app/controller/admin/report_edit.t | 556 | ||||
-rw-r--r-- | t/app/controller/admin/reportextrafields.t | 316 | ||||
-rw-r--r-- | t/app/controller/admin/search.t | 121 | ||||
-rw-r--r-- | t/app/controller/admin/states.t | 24 | ||||
-rw-r--r-- | t/app/controller/admin/stats.t | 12 | ||||
-rw-r--r-- | t/app/controller/admin/templates.t | 183 | ||||
-rw-r--r-- | t/app/controller/admin/translations.t | 191 | ||||
-rw-r--r-- | t/app/controller/admin/update_edit.t | 383 | ||||
-rw-r--r-- | t/app/controller/admin/users.t | 413 |
13 files changed, 2855 insertions, 0 deletions
diff --git a/t/app/controller/admin/bodies.t b/t/app/controller/admin/bodies.t new file mode 100644 index 000000000..9bdf8fb9a --- /dev/null +++ b/t/app/controller/admin/bodies.t @@ -0,0 +1,165 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); +$mech->log_in_ok( $superuser->email ); + +# This override is wrapped around ALL the /admin/body tests +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + MAPIT_TYPES => [ 'UTA' ], + BASE_URL => 'http://www.example.org', +}, sub { + +my $body = $mech->create_body_ok(2650, 'Aberdeen City Council'); +$mech->get_ok('/admin/body/' . $body->id); +$mech->content_contains('Aberdeen City Council'); +$mech->content_like(qr{AB\d\d}); +$mech->content_contains("http://www.example.org/around"); + +subtest 'check contact creation' => sub { + $mech->get_ok('/admin/body/' . $body->id); + + $mech->submit_form_ok( { with_fields => { + category => 'test category', + email => 'test@example.com', + note => 'test note', + non_public => undef, + state => 'unconfirmed', + } } ); + + $mech->content_contains( 'test category' ); + $mech->content_contains( 'test@example.com' ); + $mech->content_contains( '<td>test note' ); + $mech->content_like( qr/<td>\s*unconfirmed\s*<\/td>/ ); # No private + + $mech->submit_form_ok( { with_fields => { + category => 'private category', + email => 'test@example.com', + note => 'test note', + non_public => 'on', + } } ); + + $mech->content_contains( 'private category' ); + $mech->content_like( qr{test\@example.com\s*</td>\s*<td>\s*confirmed\s*<br>\s*<small>\s*Private\s*</small>\s*</td>} ); + + $mech->submit_form_ok( { with_fields => { + category => 'test/category', + email => 'test@example.com', + note => 'test/note', + non_public => 'on', + } } ); + $mech->get_ok('/admin/body/' . $body->id . '/test/category'); + $mech->content_contains('<h1>test/category</h1>'); +}; + +subtest 'check contact editing' => sub { + $mech->get_ok('/admin/body/' . $body->id .'/test%20category'); + + $mech->submit_form_ok( { with_fields => { + email => 'test2@example.com', + note => 'test2 note', + non_public => undef, + } } ); + + $mech->content_contains( 'test category' ); + $mech->content_like( qr{test2\@example.com\s*</td>\s*<td>\s*unconfirmed\s*</td>} ); + $mech->content_contains( '<td>test2 note' ); + + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->submit_form_ok( { with_fields => { + email => 'test2@example.com, test3@example.com', + note => 'test3 note', + } } ); + + $mech->content_contains( 'test2@example.com,test3@example.com' ); + + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->content_contains( '<td><strong>test2@example.com,test3@example.com' ); + + $mech->submit_form_ok( { with_fields => { + email => 'test2@example.com', + note => 'test2 note', + non_public => 'on', + } } ); + + $mech->content_like( qr{test2\@example.com\s*</td>\s*<td>\s*unconfirmed\s*<br>\s*<small>\s*Private\s*</small>\s*</td>} ); + + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->content_contains( '<td><strong>test2@example.com' ); +}; + +subtest 'check contact updating' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->content_like(qr{test2\@example.com</strong>[^<]*</td>[^<]*<td>unconfirmed}s); + + $mech->get_ok('/admin/body/' . $body->id); + + $mech->form_number( 1 ); + $mech->tick( 'confirmed', 'test category' ); + $mech->submit_form_ok({form_number => 1}); + + $mech->content_like(qr'test2@example.com</td>[^<]*<td>\s*confirmed's); + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->content_like(qr{test2\@example.com[^<]*</td>[^<]*<td><strong>confirmed}s); +}; + +$body->update({ send_method => undef }); + +subtest 'check open311 configuring' => sub { + $mech->get_ok('/admin/body/' . $body->id); + $mech->content_lacks('Council contacts configured via Open311'); + + $mech->form_number(3); + $mech->submit_form_ok( + { + with_fields => { + api_key => 'api key', + endpoint => 'http://example.com/open311', + jurisdiction => 'mySociety', + send_comments => 0, + send_method => 'Open311', + } + } + ); + $mech->content_contains('Council contacts configured via Open311'); + $mech->content_contains('Values updated'); + + my $conf = FixMyStreet::App->model('DB::Body')->find( $body->id ); + is $conf->endpoint, 'http://example.com/open311', 'endpoint configured'; + is $conf->api_key, 'api key', 'api key configured'; + is $conf->jurisdiction, 'mySociety', 'jurisdiction configures'; + + $mech->form_number(3); + $mech->submit_form_ok( + { + with_fields => { + api_key => 'new api key', + endpoint => 'http://example.org/open311', + jurisdiction => 'open311', + send_comments => 0, + send_method => 'Open311', + } + } + ); + + $mech->content_contains('Values updated'); + + $conf = FixMyStreet::App->model('DB::Body')->find( $body->id ); + is $conf->endpoint, 'http://example.org/open311', 'endpoint updated'; + is $conf->api_key, 'new api key', 'api key updated'; + is $conf->jurisdiction, 'open311', 'jurisdiction configures'; +}; + +subtest 'check text output' => sub { + $mech->get_ok('/admin/body/' . $body->id . '?text=1'); + is $mech->content_type, 'text/plain'; + $mech->content_contains('test category'); + $mech->content_lacks('<body'); +}; + + +}; # END of override wrap + +done_testing(); diff --git a/t/app/controller/admin/defecttypes.t b/t/app/controller/admin/defecttypes.t new file mode 100644 index 000000000..e7d0e42af --- /dev/null +++ b/t/app/controller/admin/defecttypes.t @@ -0,0 +1,193 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +FixMyStreet::override_config { ALLOWED_COBRANDS => ['bromley'], }, sub { + subtest 'check defecttypes menu not available' => sub { + my $body = $mech->create_body_ok( 2482, 'Bromley Council' ); + + my $user = $mech->create_user_ok( + 'bromley@example.com', + name => 'Test User', + from_body => $body + ); + + $mech->log_in_ok( $user->email ); + + $mech->get_ok('/admin'); + $mech->content_lacks('Defect Types'); + + is $mech->get('/admin/defecttypes')->code, 404, '404 if no permission'; + is $mech->get('/admin/defecttypes/' . $body->id)->code, 404, '404 if no permission'; + + $mech->log_out_ok(); + }; +}; + +FixMyStreet::override_config { ALLOWED_COBRANDS => ['oxfordshire'], }, sub { + + my $body = $mech->create_body_ok( 2237, 'Oxfordshire County Council' ); + + my $user = $mech->create_user_ok( + 'oxford@example.com', + name => 'Test User', + from_body => $body + ); + + $mech->log_in_ok( $user->email ); + + my $contact = $mech->create_contact_ok( + body_id => $body->id, + category => 'Traffic lights', + email => 'lights@example.com' + ); + + subtest 'check defecttypes menu not available without permissions' => sub { + $mech->get_ok('/admin'); + $mech->content_lacks('Defect Types'); + + is $mech->get('/admin/defecttypes')->code, 404, '404 if no permission'; + is $mech->get('/admin/defecttypes/' . $body->id)->code, 404, '404 if no permission'; + }; + + $user->user_body_permissions->create( { + body => $body, + permission_type => 'defect_type_edit', + } ); + + subtest 'check defecttypes menu available with permissions' => sub { + $mech->get_ok('/admin'); + $mech->content_contains('Defect Types'); + $mech->get_ok('/admin/defecttypes'); + is $mech->res->previous->code, 302, 'index redirects...'; + is $mech->uri->path, '/admin/defecttypes/' . $body->id, '...to body page'; + }; + + subtest 'check missing defect type is 404' => sub { + is $mech->get( '/admin/defecttypes/' . $body->id . '/299')->code, 404; + }; + + subtest 'check adding a defect type' => sub { + $mech->get_ok( '/admin/defecttypes/' . $body->id . '/new' ); + + $mech->content_contains('Traffic lights'); + + $mech->submit_form_ok( { + with_fields => { + name => 'A defect', + description => 'This is a new defect', + } } ); + + $mech->content_contains('New defect'); + }; + + subtest 'check editing a defect type' => sub { + my $defect = FixMyStreet::App->model('DB::DefectType')->search( { + name => 'A defect', + body_id => $body->id + } )->first; + + $mech->get_ok( '/admin/defecttypes/' . $body->id . '/' . $defect->id ); + + $mech->submit_form_ok( { + with_fields => { + name => 'Updated defect', + description => 'This is a new defect', + } + }, + 'submitted form' + ); + + $mech->content_lacks('A defect'); + $mech->content_contains('Updated defect'); + + my $defects = FixMyStreet::App->model('DB::DefectType')->search( { + body_id => $body->id + } ); + + is $defects->count, 1, 'only 1 defect'; + }; + + subtest 'check adding a category to a defect' => sub { + my $defect = FixMyStreet::App->model('DB::DefectType')->search( { + name => 'Updated defect', + body_id => $body->id + } )->first; + + is $defect->contact_defect_types->count, 0, + 'defect has no contact types'; + + $mech->get_ok( '/admin/defecttypes/' . $body->id . '/' . $defect->id ); + + $mech->submit_form_ok( { + with_fields => { + name => 'Updated defect', + description => 'This is a new defect', + categories => [ $contact->id ], + } + }, + 'submitted form' + ); + + $mech->content_contains('Traffic lights'); + + $defect->discard_changes; + is $defect->contact_defect_types->count, 1, 'defect has a contact type'; + is $defect->contact_defect_types->first->contact->category, + 'Traffic lights', 'defect has correct contact type'; + }; + + subtest 'check removing category from a defect' => sub { + my $defect = FixMyStreet::App->model('DB::DefectType')->search( { + name => 'Updated defect', + body_id => $body->id + } )->first; + + is $defect->contact_defect_types->count, 1, + 'defect has one contact types'; + + $mech->get_ok( '/admin/defecttypes/' . $body->id . '/' . $defect->id ); + + $mech->submit_form_ok( { + with_fields => { + name => 'Updated defect', + description => 'This is a new defect', + categories => '', + } + }, + 'submitted form' + ); + + $mech->content_lacks('Traffic lights'); + + $defect->discard_changes; + is $defect->contact_defect_types->count, 0, + 'defect has no contact type'; + }; + + subtest 'check adding codes to a defect' => sub { + my $defect = FixMyStreet::App->model('DB::DefectType')->search( { + name => 'Updated defect', + body_id => $body->id + } )->first; + + $mech->get_ok( '/admin/defecttypes/' . $body->id . '/' . $defect->id ); + + $mech->submit_form_ok( { + with_fields => { + name => 'Updated defect', + description => 'This is a new defect', + 'extra[activity_code]' => 1, + 'extra[defect_code]' => 2, + } + }, + 'submitted form' + ); + + $defect->discard_changes; + is_deeply $defect->get_extra_metadata, + { activity_code => 1, defect_code => 2 }, 'defect codes set'; + }; +}; + +done_testing(); diff --git a/t/app/controller/admin/permissions.t b/t/app/controller/admin/permissions.t new file mode 100644 index 000000000..7944cc0b1 --- /dev/null +++ b/t/app/controller/admin/permissions.t @@ -0,0 +1,196 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); + +my $bromley = $mech->create_body_ok(2482, 'Bromley Council'); + +my $dt = DateTime->new( + year => 2011, + month => 04, + day => 16, + hour => 15, + minute => 47, + second => 23 +); + +my ($report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Test', { + areas => ',2237,', +}); +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +$mech->log_in_ok( $oxfordshireuser->email ); + +subtest "Users can't edit report without report_edit permission" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->get("/admin/report_edit/$report_id"); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404, can't edit report without report_edit permission"; + }; +}; + +subtest "Users can edit report with report_edit permission" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'report_edit', + }); + + $mech->get_ok("/admin/report_edit/$report_id"); + $mech->content_contains( $report->title ); + }; +}; + +subtest "Users can't edit another council's reports with their own council's report_edit permission" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $report->bodies_str($bromley->id); + $report->cobrand('bromley'); + $report->update; + + $mech->get("/admin/report_edit/$report_id"); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404, can't edit report with incorrect body in report_edit permission"; + }; +}; + + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => [ 'oxfordshire' ], +}, sub { + my $user2_id = $user2->id; + $report->update({ bodies_str => $oxfordshire->id }); + + foreach my $perm (0, 1) { + if ($perm) { + $oxfordshireuser->user_body_permissions->find_or_create({ + body => $oxfordshire, + permission_type => 'user_edit', + }); + } + foreach my $report_user ($user, $user2) { + $report->update({ user => $report_user }); + foreach my $from_body (undef, $bromley, $oxfordshire) { + $user2->update({ from_body => $from_body }); + my $result = ($from_body || '') eq $oxfordshire || $report->user eq $user2 ? ($perm ? 200 : 404 ) : 404; + my $u = $result == 200 ? 'can' : 'cannot'; + my $b = $from_body ? $from_body->name : 'no body'; + my $p = $perm ? 'with' : 'without'; + my $r = $report->user eq $user2 ? 'with' : 'without'; + subtest "User $u edit user for $b $p permission, $r cobrand relation" => sub { + $mech->get("/admin/user_edit/$user2_id"); + my $success = $mech->res->is_success(); + ok $result == 200 ? $success : !$success, "got correct response"; + is $mech->res->code, $result, "got $result"; + }; + } + } + } + + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'user_assign_body', + }); + + subtest "Users can edit users of their own council" => sub { + $mech->get_ok("/admin/user_edit/$user2_id"); + $mech->content_contains( $user2->name ); + + # We shouldn't be able to see the permissions tick boxes + $mech->content_lacks('Moderate report details'); + + $mech->submit_form_ok( { with_fields => { + name => 'Test Updated User 2', + email => $user2->email, + body => $user2->from_body->id, + phone => '', + flagged => undef, + } } ); + $user2->discard_changes; + is $user2->name, 'Test Updated User 2', 'name changed'; + }; + + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'user_manage_permissions', + }); + + subtest "Users can edit permissions" => sub { + is $user2->user_body_permissions->count, 0, 'user2 has no permissions'; + + $mech->get_ok("/admin/user_edit/$user2_id"); + $mech->content_contains('Moderate report details'); + + $mech->submit_form_ok( { with_fields => { + name => $user2->name, + email => $user2->email, + body => $user2->from_body->id, + phone => '', + flagged => undef, + "permissions[moderate]" => 'on', + "permissions[report_edit_category]" => undef, + "permissions[report_edit_priority]" => undef, + "permissions[report_inspect]" => undef, + "permissions[report_instruct]" => undef, + "permissions[contribute_as_another_user]" => undef, + "permissions[contribute_as_anonymous_user]" => undef, + "permissions[contribute_as_body]" => undef, + "permissions[user_edit]" => undef, + "permissions[user_manage_permissions]" => undef, + "permissions[user_assign_areas]" => undef, + } } ); + + ok $user2->has_body_permission_to("moderate"), "user2 has been granted moderate permission"; + }; + + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'user_assign_areas', + }); + + subtest "Unsetting user from_body removes all permissions and area " => sub { + is $user2->user_body_permissions->count, 1, 'user2 has 1 permission'; + + $mech->get_ok("/admin/user_edit/$user2_id"); + $mech->content_contains('Moderate report details'); + + $mech->submit_form_ok( { with_fields => { + name => $user2->name, + email => $user2->email, + body => undef, + phone => '', + flagged => undef, + "permissions[moderate]" => 'on', # NB tick box is left on deliberately + "permissions[report_edit_category]" => undef, + "permissions[report_edit_priority]" => undef, + "permissions[report_inspect]" => undef, + "permissions[report_instruct]" => undef, + "permissions[contribute_as_another_user]" => undef, + "permissions[contribute_as_anonymous_user]" => undef, + "permissions[contribute_as_body]" => undef, + "permissions[user_edit]" => undef, + "permissions[user_manage_permissions]" => undef, + "permissions[user_assign_areas]" => undef, + } } ); + + is $user2->user_body_permissions->count, 0, 'user2 has had permissions removed'; + is $user2->area_id, undef, 'user2 has had area removed'; + }; +}; + +$mech->log_out_ok; + +done_testing(); diff --git a/t/app/controller/admin/priorities.t b/t/app/controller/admin/priorities.t new file mode 100644 index 000000000..4eff20be7 --- /dev/null +++ b/t/app/controller/admin/priorities.t @@ -0,0 +1,102 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); + +my $bromley = $mech->create_body_ok(2482, 'Bromley Council'); + +$mech->log_in_ok( $superuser->email ); + +subtest "response priorities can be added" => sub { + is $oxfordshire->response_priorities->count, 0, "No response priorities yet"; + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id . "/new" ); + + my $fields = { + name => "Cat 1A", + description => "Fixed within 24 hours", + deleted => undef, + is_default => undef, + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + + 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 { + my $priority_id = $oxfordshire->response_priorities->first->id; + is $oxfordshire->response_priorities->count, 1, "Response priority exists"; + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id . "/$priority_id" ); + + my $fields = { + name => "Cat 1A", + description => "Fixed within 24 hours", + deleted => undef, + is_default => 1, + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $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 priority"; + ok $oxfordshire->response_priorities->first->is_default, "Response priority set to default"; +}; + +subtest "response priorities can be listed" => sub { + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); + + $mech->content_contains( $oxfordshire->response_priorities->first->name ); + $mech->content_contains( $oxfordshire->response_priorities->first->description ); +}; + +subtest "response priorities are limited by body" => sub { + my $bromleypriority = $bromley->response_priorities->create( { + deleted => 0, + name => "Bromley Cat 0", + } ); + + is $bromley->response_priorities->count, 1, "Response 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 ); + + $mech->get_ok( "/admin/responsepriorities/" . $bromley->id ); + $mech->content_contains( $bromleypriority->name ); +}; + +$mech->log_out_ok; + +subtest "response priorities can't be viewed across councils" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $oxfordshireuser->user_body_permissions->create({ + body => $oxfordshire, + permission_type => 'responsepriority_edit', + }); + $mech->log_in_ok( $oxfordshireuser->email ); + $mech->get_ok( "/admin/responsepriorities/" . $oxfordshire->id ); + $mech->content_contains( $oxfordshire->response_priorities->first->name ); + + + $mech->get( "/admin/responsepriorities/" . $bromley->id ); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404"; + + my $bromley_priority_id = $bromley->response_priorities->first->id; + $mech->get( "/admin/responsepriorities/" . $bromley->id . "/" . $bromley_priority_id ); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404"; + }; +}; + +done_testing(); diff --git a/t/app/controller/admin/report_edit.t b/t/app/controller/admin/report_edit.t new file mode 100644 index 000000000..a8a0bd143 --- /dev/null +++ b/t/app/controller/admin/report_edit.t @@ -0,0 +1,556 @@ +use FixMyStreet::TestMech; +# avoid wide character warnings from the category change message +use open ':std', ':encoding(UTF-8)'; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); +$mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Traffic lights', email => 'lights@example.com' ); + +my $oxford = $mech->create_body_ok(2421, 'Oxford City Council'); +$mech->create_contact_ok( body_id => $oxford->id, category => 'Graffiti', email => 'graffiti@example.net' ); + +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 => '2504', + areas => ',105255,11806,11828,2247,2504,', + category => 'Other', + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + used_map => 't', + name => 'Test User', + anonymous => 'f', + external_id => '13', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + lang => 'en-gb', + service => '', + cobrand => '', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.5016605453401', + longitude => '-0.142497580865087', + user_id => $user->id, + whensent => $dt->ymd . ' ' . $dt->hms, + } +); + +$mech->log_in_ok( $superuser->email ); + +my $log_entries = FixMyStreet::App->model('DB::AdminLog')->search( + { + object_type => 'problem', + object_id => $report->id + }, + { + order_by => { -desc => 'id' }, + } +); + +is $log_entries->count, 0, 'no admin log entries'; + +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +foreach my $test ( + { + description => 'edit report title', + fields => { + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + state => 'confirmed', + name => 'Test User', + username => $user->email, + anonymous => 0, + flagged => undef, + non_public => undef, + }, + changes => { title => 'Edited Report', }, + log_entries => [qw/edit/], + resend => 0, + }, + { + description => 'edit report description', + fields => { + title => 'Edited Report', + detail => 'Detail for Report to Edit', + state => 'confirmed', + name => 'Test User', + username => $user->email, + anonymous => 0, + flagged => undef, + non_public => undef, + }, + changes => { detail => 'Edited Detail', }, + log_entries => [qw/edit edit/], + resend => 0, + }, + { + description => 'edit report user name', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Test User', + username => $user->email, + anonymous => 0, + flagged => undef, + non_public => undef, + }, + changes => { name => 'Edited User', }, + log_entries => [qw/edit edit edit/], + resend => 0, + user => $user, + }, + { + description => 'edit report set flagged true', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user->email, + anonymous => 0, + flagged => undef, + non_public => undef, + }, + changes => { + flagged => 'on', + }, + log_entries => [qw/edit edit edit edit/], + resend => 0, + user => $user, + }, + { + description => 'edit report user email', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user->email, + anonymous => 0, + flagged => 'on', + non_public => undef, + }, + changes => { username => $user2->email, }, + log_entries => [qw/edit edit edit edit edit/], + resend => 0, + user => $user2, + }, + { + description => 'change state to unconfirmed', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + 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, + }, + { + description => 'change state to confirmed', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'unconfirmed', + name => 'Edited User', + 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, + }, + { + description => 'change state to fixed', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + 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/], + resend => 0, + }, + { + description => 'change state to hidden', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'fixed', + name => 'Edited User', + 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/ + ], + resend => 0, + }, + { + description => 'edit and change state', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'hidden', + name => 'Edited User', + username => $user2->email, + anonymous => 0, + flagged => 'on', + non_public => undef, + }, + expect_comment => 1, + changes => { + state => 'confirmed', + anonymous => 1, + }, + log_entries => [ + qw/edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 0, + }, + { + description => 'resend', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user2->email, + anonymous => 1, + flagged => 'on', + non_public => undef, + }, + changes => {}, + log_entries => [ + qw/resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 1, + }, + { + description => 'non public', + fields => { + title => 'Edited Report', + detail => 'Edited Detail', + state => 'confirmed', + name => 'Edited User', + username => $user2->email, + anonymous => 1, + flagged => 'on', + non_public => undef, + }, + changes => { + non_public => 'on', + }, + log_entries => [ + qw/edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ + ], + resend => 0, + }, + { + 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"); + is_deeply( $mech->visible_form_values(), $test->{fields}, 'initial form values' ); + + my $new_fields = { + %{ $test->{fields} }, + %{ $test->{changes} }, + }; + + if ( $test->{resend} ) { + $mech->click_ok( 'resend' ); + } else { + $mech->submit_form_ok( { with_fields => $new_fields }, 'form_submitted' ); + } + + is_deeply( $mech->visible_form_values(), $new_fields, 'changed form values' ); + is $log_entries->count, scalar @{$test->{log_entries}}, 'log entry count'; + is $log_entries->next->action, $_, 'log entry added' for @{ $test->{log_entries} }; + + $report->discard_changes; + + if ($report->state eq 'confirmed' && $report->whensent) { + $mech->content_contains( 'type="submit" name="resend"', 'resend button' ); + } else { + $mech->content_lacks( 'type="submit" name="resend"', 'no resend button' ); + } + + $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 'username' } keys %{ $test->{changes} }; + + if ( $test->{user} ) { + is $report->user->id, $test->{user}->id, 'user changed'; + } + + if ( $test->{resend} ) { + $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; + }; +} + +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'fixmystreet', +}, sub { + +subtest 'change report category' => sub { + my ($ox_report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Unsure', { + category => 'Potholes', + areas => ',2237,2421,', # Cached used by categories_for_point... + latitude => 51.7549262252, + longitude => -1.25617899435, + whensent => \'current_timestamp', + }); + $mech->get_ok("/admin/report_edit/" . $ox_report->id); + + $mech->submit_form_ok( { with_fields => { category => 'Traffic lights' } }, 'form_submitted' ); + $ox_report->discard_changes; + is $ox_report->category, 'Traffic lights'; + isnt $ox_report->whensent, undef; + 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; + is $ox_report->category, 'Graffiti'; + is $ox_report->whensent, undef; +}; + +}; + +subtest 'change email to new user' => sub { + $log_entries->delete; + $mech->get_ok("/admin/report_edit/$report_id"); + my $fields = { + title => $report->title, + detail => $report->detail, + state => $report->state, + name => $report->name, + username => $report->user->email, + category => 'Potholes', + anonymous => 1, + flagged => 'on', + non_public => 'on', + external_id => '13', + external_body => '', + external_team => '', + }; + + is_deeply( $mech->visible_form_values(), $fields, 'initial form values' ); + + my $changes = { + username => 'test3@example.com' + }; + + my $user3 = FixMyStreet::App->model('DB::User')->find( { email => 'test3@example.com' } ); + + ok !$user3, 'user not in database'; + + my $new_fields = { + %{ $fields }, + %{ $changes }, + }; + + $mech->submit_form_ok( + { + with_fields => $new_fields, + } + ); + + is $log_entries->count, 1, 'created admin log entries'; + 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' } ); + + $report->discard_changes; + + ok $user3, 'new user created'; + is $report->user_id, $user3->id, 'user changed to new user'; +}; + +subtest 'adding email to abuse list from report page' => sub { + my $email = $report->user->email; + + my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + $abuse->delete if $abuse; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Ban user'); + + $mech->click_ok('banuser'); + + $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>User in abuse table</small>'); +}; + +subtest 'flagging user from report page' => sub { + $report->user->flagged(0); + $report->user->update; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Flag user'); + + $mech->click_ok('flaguser'); + + $mech->content_contains('User flagged'); + $mech->content_contains('Remove flag'); + + $report->discard_changes; + ok $report->user->flagged, 'user flagged'; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Remove flag'); +}; + +subtest 'unflagging user from report page' => sub { + $report->user->flagged(1); + $report->user->update; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Remove flag'); + + $mech->click_ok('removeuserflag'); + + $mech->content_contains('User flag removed'); + $mech->content_contains('Flag user'); + + $report->discard_changes; + ok !$report->user->flagged, 'user not flagged'; + + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains('Flag user'); +}; + +subtest "Test setting a report from unconfirmed to something else doesn't cause a front end error" => sub { + $report->update( { confirmed => undef, state => 'unconfirmed', non_public => 0 } ); + $mech->get_ok("/admin/report_edit/$report_id"); + $mech->submit_form_ok( { with_fields => { state => 'investigating' } } ); + $report->discard_changes; + ok( $report->confirmed, 'report has a confirmed timestamp' ); + $mech->get_ok("/report/$report_id"); +}; + +done_testing(); diff --git a/t/app/controller/admin/reportextrafields.t b/t/app/controller/admin/reportextrafields.t new file mode 100644 index 000000000..fb06665f4 --- /dev/null +++ b/t/app/controller/admin/reportextrafields.t @@ -0,0 +1,316 @@ +use strict; +use warnings; + +package FixMyStreet::Cobrand::Tester; + +use parent 'FixMyStreet::Cobrand::FixMyStreet'; + +sub allow_report_extra_fields { 1 } + +sub area_types { [ 'UTA' ] } + + +package FixMyStreet::Cobrand::SecondTester; + +use parent 'FixMyStreet::Cobrand::FixMyStreet'; + +sub allow_report_extra_fields { 1 } + +sub area_types { [ 'UTA' ] } + + +package FixMyStreet::Cobrand::NoExtras; + +use parent 'FixMyStreet::Cobrand::FixMyStreet'; + +sub allow_report_extra_fields { 0 } + +sub area_types { [ 'UTA' ] } + +package main; + +use FixMyStreet::TestMech; + +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); +my $body = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $contact = $mech->create_contact_ok( body_id => $body->id, category => 'Potholes', email => 'potholes@example.com' ); + +my $body2 = $mech->create_body_ok(2651, 'Edinburgh City Council'); +my $contact2 = $mech->create_contact_ok( body_id => $body2->id, category => 'Potholes', email => 'potholes@example.com' ); + + +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'tester' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + LANGUAGES => [ + 'en-gb,English,en_GB', + 'de,German,de_DE' + ] +}, sub { + $mech->log_in_ok( $user->email ); + + subtest 'add extra fields to Contacts' => sub { + my $contact_extra_fields = []; + + is_deeply $contact->get_extra_fields, $contact_extra_fields, 'contact has empty extra fields'; + $mech->get_ok("/admin/body/" . $body->id . "/" . $contact->category); + + $mech->submit_form_ok( { with_fields => { + "metadata[0].order" => "1", + "metadata[0].code" => "string_test", + "metadata[0].required" => "on", + "metadata[0].notice" => "", + "metadata[0].description" => "this is a test description", + "metadata[0].datatype_description" => "hint here", + "metadata[0].datatype" => "string", + "note" => "Added extra field", + }}); + $mech->content_contains('Values updated'); + + push @$contact_extra_fields, { + order => "1", + code => "string_test", + required => "true", + variable => "true", + description => "this is a test description", + datatype_description => "hint here", + datatype => "string", + }; + $contact->discard_changes; + is_deeply $contact->get_extra_fields, $contact_extra_fields, 'new string field was added'; + + + $mech->get_ok("/admin/body/" . $body->id . "/" . $contact->category); + $mech->submit_form_ok( { with_fields => { + "metadata[1].order" => "2", + "metadata[1].code" => "list_test", + "metadata[1].required" => undef, + "metadata[1].notice" => "", + "metadata[1].description" => "this field is a list", + "metadata[1].datatype_description" => "", + "metadata[1].datatype" => "list", + "metadata[1].values[0].key" => "key1", + "metadata[1].values[0].name" => "name1", + "note" => "Added extra list field", + }}); + $mech->content_contains('Values updated'); + + push @$contact_extra_fields, { + order => "2", + code => "list_test", + required => "false", + variable => "true", + description => "this field is a list", + datatype_description => "", + datatype => "singlevaluelist", + values => [ + { name => "name1", key => "key1" }, + ] + }; + $contact->discard_changes; + is_deeply $contact->get_extra_fields, $contact_extra_fields, 'new list field was added'; + + $contact->set_extra_fields(); + $contact->update; + }; + + subtest 'Create and update new ReportExtraFields' => sub { + my $extra_fields = []; + + my $model = FixMyStreet::App->model('DB::ReportExtraFields'); + is $model->count, 0, 'no ReportExtraFields yet'; + + $mech->get_ok("/admin/reportextrafields/new"); + $mech->submit_form_ok({ with_fields => { + name => "Test extra fields", + cobrand => "tester", + language => undef, + "metadata[0].order" => "1", + "metadata[0].code" => "string_test", + "metadata[0].required" => "on", + "metadata[0].notice" => "", + "metadata[0].description" => "this is a test description", + "metadata[0].datatype_description" => "hint here", + "metadata[0].datatype" => "string", + }}); + is $model->count, 1, 'new ReportExtraFields created'; + + my $object = $model->first; + push @$extra_fields, { + order => "1", + code => "string_test", + required => "true", + variable => "true", + description => "this is a test description", + datatype_description => "hint here", + datatype => "string", + }; + is_deeply $object->get_extra_fields, $extra_fields, 'new string field was added'; + is $object->cobrand, 'tester', 'Correct cobrand set'; + is $object->language, undef, 'Correct language set'; + + $mech->get_ok("/admin/reportextrafields/" . $object->id); + $mech->submit_form_ok( { with_fields => { + "language" => "en-gb", + "metadata[1].order" => "2", + "metadata[1].code" => "list_test", + "metadata[1].required" => undef, + "metadata[1].notice" => "", + "metadata[1].description" => "this field is a list", + "metadata[1].datatype_description" => "", + "metadata[1].datatype" => "list", + "metadata[1].values[0].key" => "key1", + "metadata[1].values[0].name" => "name1", + }}); + + push @$extra_fields, { + order => "2", + code => "list_test", + required => "false", + variable => "true", + description => "this field is a list", + datatype_description => "", + datatype => "singlevaluelist", + values => [ + { name => "name1", key => "key1" }, + ] + }; + $object->discard_changes; + is_deeply $object->get_extra_fields, $extra_fields, 'new list field was added'; + is $object->language, "en-gb", "Correct language was set"; + + $mech->get_ok("/admin/reportextrafields/" . $object->id); + $mech->submit_form_ok( { with_fields => { + "metadata[1].values[1].key" => "key2", + "metadata[1].values[1].name" => "name2", + }}); + + push @{$extra_fields->[1]->{values}}, { name => "name2", key => "key2" }; + $object->discard_changes; + is_deeply $object->get_extra_fields, $extra_fields, 'options can be added to list field'; + }; + + subtest 'Fields appear on /report/new' => sub { + $mech->get_ok("/report/new?longitude=-1.351488&latitude=51.847235&category=" . $contact->category); + $mech->content_contains("this is a test description"); + $mech->content_contains("this field is a list"); + }; +}; + +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'tester' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + LANGUAGES => [ 'de,German,de_DE' ] +}, sub { + subtest 'Language-specific fields are missing from /report/new for other language' => sub { + $mech->get_ok("/report/new?longitude=-1.351488&latitude=51.847235&category=" . $contact->category); + $mech->content_lacks("this is a test description"); + $mech->content_lacks("this field is a list"); + }; +}; + +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'secondtester' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + LANGUAGES => [ 'en-gb,English,en_GB' ] +}, sub { + subtest 'Cobrand-specific fields are missing from /report/new for other cobrand' => sub { + $mech->get_ok("/report/new?longitude=-1.351488&latitude=51.847235&category=" . $contact->category); + $mech->content_lacks("this is a test description"); + $mech->content_lacks("this field is a list"); + }; +}; + +FixMyStreet::override_config { + ALLOWED_COBRANDS => [ { 'noextras' => '.' } ], + MAPIT_URL => 'http://mapit.uk/', + LANGUAGES => [ 'en-gb,English,en_GB' ] +}, sub { + subtest "Extra fields are missing from cobrand that doesn't allow them" => sub { + my $object = FixMyStreet::App->model('DB::ReportExtraFields')->first; + $object->update({ language => "", cobrand => ""}); + + $mech->get_ok("/report/new?longitude=-1.351488&latitude=51.847235&category=" . $contact->category); + $mech->content_lacks("this is a test description"); + $mech->content_lacks("this field is a list"); + }; +}; + +FixMyStreet::App->model('DB::ReportExtraFields')->delete_all; +$mech->log_out_ok; + +subtest 'Reports are created with correct extra metadata' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'tester' ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + my $model = FixMyStreet::App->model('DB::ReportExtraFields'); + my $extra_fields = $model->find_or_create({ + name => "Test extra fields", + language => "", + cobrand => "" + }); + $extra_fields->push_extra_fields({ + order => "1", + code => "string_test", + required => "true", + variable => "true", + description => "this is a test description", + datatype_description => "hint here", + datatype => "string", + }); + $extra_fields->push_extra_fields({ + order => "2", + code => "list_test", + required => "false", + variable => "true", + description => "this field is a list", + datatype_description => "", + datatype => "singlevaluelist", + values => [ + { name => "name1", key => "key1" }, + ] + }); + $extra_fields->update; + + my $user = $mech->create_user_ok('testuser@example.com', name => 'Test User'); + $mech->log_in_ok($user->email); + + $mech->get_ok('/report/new?latitude=55.952055&longitude=-3.189579'); + $mech->content_contains($contact2->category); + + my $extra_id = $extra_fields->id; + $mech->submit_form_ok( { + with_fields => { + title => "Test Report", + detail => "This is a test report", + category => $contact2->category, + "extra[$extra_id]string_test" => "Problem meta string", + "extra[$extra_id]list_test" => "key1", + } + } ); + + my $report = $user->problems->first; + is_deeply $report->get_extra_fields, [ + { + name => 'string_test', + description => 'this is a test description', + value => 'Problem meta string', + }, + { + name => 'list_test', + description => 'this field is a list', + value => 'key1', + } + ], 'Report has correct extra data'; + }; +}; + + +done_testing(); diff --git a/t/app/controller/admin/search.t b/t/app/controller/admin/search.t new file mode 100644 index 000000000..497ac9fd6 --- /dev/null +++ b/t/app/controller/admin/search.t @@ -0,0 +1,121 @@ +use FixMyStreet::TestMech; +# avoid wide character warnings from the category change message +use open ':std', ':encoding(UTF-8)'; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); +$mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Traffic lights', email => 'lights@example.com' ); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); + +my $oxford = $mech->create_body_ok(2421, 'Oxford City Council'); +$mech->create_contact_ok( body_id => $oxford->id, category => 'Graffiti', email => 'graffiti@example.net' ); + +my $bromley = $mech->create_body_ok(2482, 'Bromley Council'); + +my $user3 = FixMyStreet::App->model('DB::User')->create( { email => 'test3@example.com' } ); + +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 => '2504', + areas => ',105255,11806,11828,2247,2504,', + category => 'Other', + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + used_map => 't', + name => 'Test User', + anonymous => 'f', + external_id => '13', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + lang => 'en-gb', + service => '', + cobrand => '', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.5016605453401', + longitude => '-0.142497580865087', + user_id => $user->id, + whensent => $dt->ymd . ' ' . $dt->hms, + } +); +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +$mech->log_in_ok( $superuser->email ); + +subtest 'show flagged entries' => sub { + $report->flagged( 1 ); + $report->update; + $mech->get_ok('/admin/flagged'); + $mech->content_contains( $report->title ); +}; + +my $update = FixMyStreet::App->model('DB::Comment')->create( + { + text => 'this is an update', + user => $user, + state => 'confirmed', + problem => $report, + mark_fixed => 0, + anonymous => 1, + } +); + +subtest 'report search' => sub { + $mech->get_ok('/admin/reports'); + $mech->get_ok('/admin/reports?search=' . $report->id ); + + $mech->content_contains( $report->title ); + my $r_id = $report->id; + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + + $mech->get_ok('/admin/reports?search=' . $report->external_id); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + + $mech->get_ok('/admin/reports?search=ref:' . $report->external_id); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + + $mech->get_ok('/admin/reports?search=' . $report->user->email); + + my $u_id = $update->id; + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id#update_$u_id"[^>]*>$u_id</a>} ); + + $update->state('hidden'); + $update->update; + + $mech->get_ok('/admin/reports?search=' . $report->user->email); + $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $u_id \s* </td>}xs ); + + $report->state('hidden'); + $report->update; + + $mech->get_ok('/admin/reports?search=' . $report->user->email); + $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td[^>]*> \s* $r_id \s* </td>}xs ); + + $report->state('fixed - user'); + $report->update; + + $mech->get_ok('/admin/reports?search=' . $report->user->email); + $mech->content_like( qr{href="http://[^/]*[^.]/report/$r_id"[^>]*>$r_id</a>} ); +}; + +done_testing(); diff --git a/t/app/controller/admin/states.t b/t/app/controller/admin/states.t new file mode 100644 index 000000000..60ffe5b88 --- /dev/null +++ b/t/app/controller/admin/states.t @@ -0,0 +1,24 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +$mech->log_in_ok( $user->email ); + +subtest 'basic states admin' => sub { + $mech->get_ok('/admin/states'); + $mech->submit_form_ok({ button => 'new', with_fields => { label => 'third party', type => 'closed', name => 'Third party referral' } }); + $mech->content_contains('Third party referral'); + $mech->content_contains('Fixed'); + $mech->submit_form_ok({ button => 'delete:fixed' }); + $mech->content_lacks('Fixed'); + $mech->submit_form_ok({ form_number => 2, button => 'new_fixed' }); + $mech->content_contains('Fixed'); + $mech->submit_form_ok({ with_fields => { 'name:third party' => 'Third party incident' } }); + $mech->content_contains('Third party incident'); +}; + +# TODO Language tests + +done_testing; diff --git a/t/app/controller/admin/stats.t b/t/app/controller/admin/stats.t new file mode 100644 index 000000000..dae51d31f --- /dev/null +++ b/t/app/controller/admin/stats.t @@ -0,0 +1,12 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +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/admin/templates.t b/t/app/controller/admin/templates.t new file mode 100644 index 000000000..179a1afcb --- /dev/null +++ b/t/app/controller/admin/templates.t @@ -0,0 +1,183 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $oxfordshirecontact = $mech->create_contact_ok( body_id => $oxfordshire->id, category => 'Potholes', email => 'potholes@example.com' ); +my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire); + +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 => '2504', + areas => ',105255,11806,11828,2247,2504,', + category => 'Other', + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + used_map => 't', + name => 'Test User', + anonymous => 'f', + external_id => '13', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + lang => 'en-gb', + service => '', + cobrand => '', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.5016605453401', + longitude => '-0.142497580865087', + user_id => $user->id, + whensent => $dt->ymd . ' ' . $dt->hms, + } +); + +$mech->log_in_ok( $superuser->email ); + +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +subtest "response templates can be added" => sub { + is $oxfordshire->response_templates->count, 0, "No response templates yet"; + $mech->log_in_ok( $superuser->email ); + $mech->get_ok( "/admin/templates/" . $oxfordshire->id . "/new" ); + + my $fields = { + title => "Report acknowledgement", + text => "Thank you for your report. We will respond shortly.", + auto_response => undef, + "contacts[".$oxfordshirecontact->id."]" => 1, + }; + $mech->submit_form_ok( { with_fields => $fields } ); + + is $oxfordshire->response_templates->count, 1, "Response template was added"; +}; + +subtest "response templates are included on page" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $report->update({ category => $oxfordshirecontact->category, bodies_str => $oxfordshire->id }); + $mech->log_in_ok( $oxfordshireuser->email ); + + $mech->get_ok("/report/" . $report->id); + $mech->content_contains( $oxfordshire->response_templates->first->text ); + + $mech->log_out_ok; + }; +}; + +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"; +}; + +done_testing(); diff --git a/t/app/controller/admin/translations.t b/t/app/controller/admin/translations.t new file mode 100644 index 000000000..f5c32baa6 --- /dev/null +++ b/t/app/controller/admin/translations.t @@ -0,0 +1,191 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +$mech->log_in_ok( $superuser->email ); + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + MAPIT_TYPES => [ 'UTA' ], +}, sub { + +my $body = $mech->create_body_ok(2650, 'Aberdeen City Council'); +$mech->create_contact_ok( body_id => $body->id, category => 'Traffic lights', email => 'lights@example.com' ); + +subtest 'check no translations if one language' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_lacks( 'Translations' ); + +}; + +}; + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + MAPIT_TYPES => [ 'UTA' ], + LANGUAGES => [ + 'en-gb,English,en_GB', + 'de,German,de_DE' + ] +}, sub { + +my $body = $mech->create_body_ok(2650, 'Aberdeen City Council'); +$mech->create_contact_ok( body_id => $body->id, category => 'Traffic lights', email => 'lights@example.com' ); + +my $body2 = $mech->create_body_ok(2643, 'Arun District Council'); + +FixMyStreet::DB->resultset("Translation")->create({ + lang => "de", + tbl => "body", + object_id => $body2->id, + col => "name", + msgstr => "DE Arun", +}); + +subtest 'check translations if multiple languages' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_contains( 'Translations' ); +}; + +subtest 'check add category with translation' => sub { + $mech->get_ok('/admin/body/' . $body2->id); + + $mech->content_contains('DE Arun'); + + $mech->submit_form_ok( { with_fields => { + category => 'Potholes', + translation_de => 'DE potholes', + email => 'potholes@example.org', + } } ); + + # check that error page includes translations + $mech->content_lacks('DE Arun'); + $mech->content_contains('DE potholes'); + + $mech->submit_form_ok( { with_fields => { + category => 'Potholes', + translation_de => 'DE potholes', + email => 'potholes@example.org', + note => 'adding category with translation', + } } ); + + $mech->content_contains('DE Arun'); + $mech->content_lacks('DE potholes'); + + $mech->get_ok('/admin/body/' . $body2->id . '/Potholes'); + + $mech->content_contains( 'DE potholes' ); +}; + +subtest 'check add category translation' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_lacks( 'DE Traffic lights' ); + + $mech->submit_form_ok( { with_fields => { + translation_de => 'DE Traffic lights', + note => 'updating translation', + } } ); + + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_contains( 'DE Traffic lights' ); +}; + +subtest 'check replace category translation' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_contains( 'DE Traffic lights' ); + + $mech->submit_form_ok( { with_fields => { + translation_de => 'German Traffic lights', + note => 'updating translation', + } } ); + + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_lacks( 'DE Traffic lights' ); + $mech->content_contains( 'German Traffic lights' ); +}; + +subtest 'delete category translation' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + $mech->content_contains( 'German Traffic lights' ); + + $mech->submit_form_ok( { with_fields => { + translation_de => '', + note => 'updating translation', + } } ); + + $mech->get_ok('/admin/body/' . $body->id . '/Traffic%20lights'); + + $mech->content_lacks( 'DE German Traffic lights' ); +}; + +subtest 'check add body translation' => sub { + $mech->get_ok('/admin/body/' . $body->id); + + $mech->content_lacks( 'DE Aberdeen' ); + + $mech->submit_form_ok( { with_fields => { + send_method => 'email', + translation_de => 'DE Aberdeen', + } } ); + + $mech->content_contains( 'DE Aberdeen' ); +}; + +subtest 'check replace body translation' => sub { + $mech->get_ok('/admin/body/' . $body->id); + + $mech->content_contains( 'DE Aberdeen' ); + + $mech->submit_form_ok( { with_fields => { + send_method => 'email', + translation_de => 'German Aberdeen', + } } ); + + $mech->content_lacks( 'DE Aberdeen' ); + $mech->content_contains( 'German Aberdeen' ); +}; + +subtest 'delete body translation' => sub { + $mech->get_ok('/admin/body/' . $body->id); + $mech->content_contains( 'German Aberdeen' ); + + $mech->submit_form_ok( { with_fields => { + send_method => 'email', + translation_de => '', + } } ); + + $mech->content_lacks( 'DE German Aberdeen' ); +}; + +subtest 'check add body with translation' => sub { + $mech->get_ok('/admin/bodies/'); + $mech->submit_form_ok( { with_fields => { + area_ids => 2643, + send_method => 'email', + translation_de => 'DE A Body', + } } ); + + # check that error page includes translations + $mech->content_contains( 'DE A Body' ); + + $mech->submit_form_ok( { with_fields => { + name => 'A body', + area_ids => 2643, + send_method => 'email', + translation_de => 'DE A Body', + } } ); + + $mech->follow_link_ok({ text => 'A body' }); + $mech->content_contains( 'DE A Body' ); +} +}; + +done_testing(); diff --git a/t/app/controller/admin/update_edit.t b/t/app/controller/admin/update_edit.t new file mode 100644 index 000000000..6ddbdbdfc --- /dev/null +++ b/t/app/controller/admin/update_edit.t @@ -0,0 +1,383 @@ +use FixMyStreet::TestMech; +# avoid wide character warnings from the category change message +use open ':std', ':encoding(UTF-8)'; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $user3 = FixMyStreet::App->model('DB::User')->create( { email => 'test3@example.com' } ); + +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 => '2504', + areas => ',105255,11806,11828,2247,2504,', + category => 'Other', + title => 'Report to Edit', + detail => 'Detail for Report to Edit', + used_map => 't', + name => 'Test User', + anonymous => 'f', + external_id => '13', + state => 'confirmed', + confirmed => $dt->ymd . ' ' . $dt->hms, + lang => 'en-gb', + service => '', + cobrand => '', + cobrand_data => '', + send_questionnaire => 't', + latitude => '51.5016605453401', + longitude => '-0.142497580865087', + user_id => $user->id, + whensent => $dt->ymd . ' ' . $dt->hms, + } +); + +$mech->log_in_ok( $superuser->email ); + +my $report_id = $report->id; +ok $report, "created test report - $report_id"; + +my $update = FixMyStreet::App->model('DB::Comment')->create( + { + text => 'this is an update', + user => $user, + state => 'confirmed', + problem => $report, + mark_fixed => 0, + anonymous => 1, + } +); + +my $log_entries = FixMyStreet::App->model('DB::AdminLog')->search( + { + object_type => 'update', + object_id => $update->id + }, + { + order_by => { -desc => 'id' }, + } +); + +is $log_entries->count, 0, 'no admin log entries'; + +for my $test ( + { + desc => 'edit update text', + fields => { + text => 'this is an update', + state => 'confirmed', + name => '', + anonymous => 1, + username => 'test@example.com', + }, + changes => { + text => 'this is a changed update', + }, + log_count => 1, + log_entries => [qw/edit/], + }, + { + desc => 'edit update name', + fields => { + text => 'this is a changed update', + state => 'confirmed', + name => '', + anonymous => 1, + username => 'test@example.com', + }, + changes => { + name => 'A User', + }, + log_count => 2, + log_entries => [qw/edit edit/], + }, + { + desc => 'edit update anonymous', + fields => { + text => 'this is a changed update', + state => 'confirmed', + name => 'A User', + anonymous => 1, + username => 'test@example.com', + }, + changes => { + anonymous => 0, + }, + log_count => 3, + log_entries => [qw/edit edit edit/], + }, + { + desc => 'edit update user', + fields => { + text => 'this is a changed update', + state => 'confirmed', + name => 'A User', + anonymous => 0, + username => 'test@example.com', + }, + changes => { + username => 'test2@example.com', + }, + log_count => 4, + log_entries => [qw/edit edit edit edit/], + user => $user2, + }, + { + desc => 'edit update state', + fields => { + text => 'this is a changed update', + state => 'confirmed', + name => 'A User', + anonymous => 0, + username => 'test2@example.com', + }, + changes => { + state => 'unconfirmed', + }, + log_count => 5, + log_entries => [qw/state_change edit edit edit edit/], + }, + { + desc => 'edit update state and text', + fields => { + text => 'this is a changed update', + state => 'unconfirmed', + name => 'A User', + anonymous => 0, + username => 'test2@example.com', + }, + changes => { + text => 'this is a twice changed update', + state => 'confirmed', + }, + log_count => 7, + log_entries => [qw/edit state_change state_change edit edit edit edit/], + }, +) { + subtest $test->{desc} => sub { + $log_entries->reset; + $mech->get_ok('/admin/update_edit/' . $update->id ); + + is_deeply $mech->visible_form_values, $test->{fields}, 'initial form values'; + + my $to_submit = { + %{ $test->{fields} }, + %{ $test->{changes} } + }; + + $mech->submit_form_ok( { with_fields => $to_submit } ); + + is_deeply $mech->visible_form_values, $to_submit, 'submitted form values'; + + is $log_entries->count, $test->{log_count}, 'number of log entries'; + is $log_entries->next->action, $_, 'log action' for @{ $test->{log_entries} }; + + $update->discard_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; + } + + if ( $test->{user} ) { + is $update->user->id, $test->{user}->id, 'update user'; + } + }; +} + +my $westminster = $mech->create_body_ok(2504, 'Westminster City Council'); +$report->bodies_str($westminster->id); +$report->update; + +for my $test ( + { + desc => 'user is problem owner', + problem_user => $user, + update_user => $user, + update_fixed => 0, + update_reopen => 0, + update_state => undef, + user_body => undef, + content => 'user is problem owner', + }, + { + desc => 'user is body user', + problem_user => $user, + update_user => $user2, + update_fixed => 0, + update_reopen => 0, + update_state => undef, + user_body => $westminster->id, + content => 'user is from same council as problem - ' . $westminster->id, + }, + { + desc => 'update changed problem state', + problem_user => $user, + update_user => $user2, + update_fixed => 0, + update_reopen => 0, + update_state => 'planned', + user_body => $westminster->id, + content => 'Update changed problem state to planned', + }, + { + desc => 'update marked problem as fixed', + problem_user => $user, + update_user => $user3, + update_fixed => 1, + update_reopen => 0, + update_state => undef, + user_body => undef, + content => 'Update marked problem as fixed', + }, + { + desc => 'update reopened problem', + problem_user => $user, + update_user => $user, + update_fixed => 0, + update_reopen => 1, + update_state => undef, + user_body => undef, + content => 'Update reopened problem', + }, +) { + subtest $test->{desc} => sub { + $report->user( $test->{problem_user} ); + $report->update; + + $update->user( $test->{update_user} ); + $update->problem_state( $test->{update_state} ); + $update->mark_fixed( $test->{update_fixed} ); + $update->mark_open( $test->{update_reopen} ); + $update->update; + + $test->{update_user}->from_body( $test->{user_body} ); + $test->{update_user}->update; + + $mech->get_ok('/admin/update_edit/' . $update->id ); + $mech->content_contains( $test->{content} ); + }; +} + +subtest 'editing update email creates new user if required' => sub { + my $user = FixMyStreet::App->model('DB::User')->find( { email => 'test4@example.com' } ); + + $user->delete if $user; + + my $fields = { + text => 'this is a changed update', + state => 'confirmed', + name => 'A User', + anonymous => 0, + username => 'test4@example.com', + }; + + $mech->submit_form_ok( { with_fields => $fields } ); + + $user = FixMyStreet::App->model('DB::User')->find( { email => 'test4@example.com' } ); + + is_deeply $mech->visible_form_values, $fields, 'submitted form values'; + + ok $user, 'new user created'; + + $update->discard_changes; + is $update->user->id, $user->id, 'update set to new user'; +}; + +subtest 'adding email to abuse list from update page' => sub { + my $email = $update->user->email; + + my $abuse = FixMyStreet::App->model('DB::Abuse')->find( { email => $email } ); + $abuse->delete if $abuse; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Ban user'); + + $mech->click_ok('banuser'); + + $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>User in abuse table</small>'); +}; + +subtest 'flagging user from update page' => sub { + $update->user->flagged(0); + $update->user->update; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Flag user'); + + $mech->click_ok('flaguser'); + + $mech->content_contains('User flagged'); + $mech->content_contains('Remove flag'); + + $update->discard_changes; + ok $update->user->flagged, 'user flagged'; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Remove flag'); +}; + +subtest 'unflagging user from update page' => sub { + $update->user->flagged(1); + $update->user->update; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Remove flag'); + + $mech->click_ok('removeuserflag'); + + $mech->content_contains('User flag removed'); + $mech->content_contains('Flag user'); + + $update->discard_changes; + ok !$update->user->flagged, 'user not flagged'; + + $mech->get_ok( '/admin/update_edit/' . $update->id ); + $mech->content_contains('Flag user'); +}; + +subtest 'hiding comment marked as fixed reopens report' => sub { + $update->mark_fixed( 1 ); + $update->update; + + $report->state('fixed - user'); + $report->update; + + my $fields = { + text => 'this is a changed update', + state => 'hidden', + name => 'A User', + anonymous => 0, + username => 'test2@example.com', + }; + + $mech->submit_form_ok( { with_fields => $fields } ); + + $report->discard_changes; + is $report->state, 'confirmed', 'report reopened'; + $mech->content_contains('Problem marked as open'); +}; + +done_testing(); diff --git a/t/app/controller/admin/users.t b/t/app/controller/admin/users.t new file mode 100644 index 000000000..e6cf51449 --- /dev/null +++ b/t/app/controller/admin/users.t @@ -0,0 +1,413 @@ +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1); + +my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $haringey = $mech->create_body_ok(2509, 'Haringey Borough Council'); +my $southend = $mech->create_body_ok(2607, 'Southend-on-Sea Borough Council'); + +$mech->log_in_ok( $superuser->email ); + +subtest 'search abuse' => sub { + my $abuse = FixMyStreet::App->model('DB::Abuse')->find_or_create( { email => $user->email } ); + $mech->get_ok( '/admin/users?search=example' ); + $mech->content_like(qr{test\@example.com.*</td>\s*<td>.*?</td>\s*<td>User in abuse table}s); +}; + +subtest 'remove user from abuse list from edit user page' => sub { + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->content_contains('User in abuse table'); + + $mech->click_ok('unban'); + + my $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 'show flagged entries' => sub { + $user->flagged( 1 ); + $user->update; + + $mech->get_ok('/admin/flagged'); + $mech->content_contains( $user->email ); + + $user->flagged( 0 ); + $user->update; +}; + +subtest 'user search' => sub { + $mech->get_ok('/admin/users'); + $mech->get_ok('/admin/users?search=' . $user->name); + + $mech->content_contains( $user->name); + my $u_id = $user->id; + $mech->content_like( qr{user_edit/$u_id">Edit</a>} ); + + $mech->get_ok('/admin/users?search=' . $user->email); + + $mech->content_like( qr{user_edit/$u_id">Edit</a>} ); + + $user->from_body($haringey->id); + $user->update; + $mech->get_ok('/admin/users?search=' . $haringey->id ); + $mech->content_contains('Haringey'); +}; + +subtest 'search does not show user from another council' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->get_ok('/admin/users'); + $mech->get_ok('/admin/users?search=' . $user->name); + + $mech->content_contains( "Searching found no users." ); + + $mech->get_ok('/admin/users?search=' . $user->email); + $mech->content_contains( "Searching found no users." ); + }; +}; + +subtest 'user_edit does not show user from another council' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'oxfordshire' ], + }, sub { + $mech->get('/admin/user_edit/' . $user->id); + ok !$mech->res->is_success(), "want a bad response"; + is $mech->res->code, 404, "got 404"; + }; +}; + +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, + "permissions[report_edit]" => undef, + "permissions[report_edit_category]" => undef, + "permissions[report_edit_priority]" => undef, + "permissions[report_inspect]" => undef, + "permissions[report_instruct]" => undef, + "permissions[contribute_as_another_user]" => undef, + "permissions[contribute_as_anonymous_user]" => undef, + "permissions[contribute_as_body]" => undef, + "permissions[view_body_contribute_details]" => undef, + "permissions[user_edit]" => undef, + "permissions[user_manage_permissions]" => undef, + "permissions[user_assign_body]" => undef, + "permissions[user_assign_areas]" => undef, + "permissions[template_edit]" => undef, + "permissions[responsepriority_edit]" => undef, + "permissions[category_edit]" => undef, + trusted_bodies => undef, +); + +# 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 { + for my $test ( + { + desc => 'edit user name', + fields => { + name => '', + email => 'test@example.com', + body => $haringey->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + name => 'Changed User', + }, + log_count => 1, + log_entries => [qw/edit/], + }, + { + desc => 'edit user email', + fields => { + name => 'Changed User', + email => 'test@example.com', + body => $haringey->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + email => 'changed@example.com', + }, + log_count => 2, + log_entries => [qw/edit edit/], + }, + { + desc => 'edit user body', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $haringey->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + body => $southend->id, + }, + log_count => 3, + log_entries => [qw/edit edit edit/], + }, + { + desc => 'edit user flagged', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $southend->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + flagged => 'on', + }, + log_count => 4, + log_entries => [qw/edit edit edit edit/], + }, + { + desc => 'edit user remove flagged', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $southend->id, + phone => '', + flagged => 'on', + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + flagged => undef, + }, + log_count => 4, + log_entries => [qw/edit edit edit edit/], + }, + { + desc => 'edit user add is_superuser', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $southend->id, + phone => '', + flagged => undef, + is_superuser => undef, + area_id => '', + %default_perms, + }, + changes => { + is_superuser => 'on', + }, + removed => [ + keys %default_perms, + ], + log_count => 5, + log_entries => [qw/edit edit edit edit edit/], + }, + { + desc => 'edit user remove is_superuser', + fields => { + name => 'Changed User', + email => 'changed@example.com', + body => $southend->id, + phone => '', + flagged => undef, + is_superuser => 'on', + area_id => '', + }, + changes => { + is_superuser => undef, + }, + added => { + %default_perms, + }, + log_count => 5, + log_entries => [qw/edit edit edit edit edit/], + }, + ) { + subtest $test->{desc} => sub { + $mech->get_ok( '/admin/user_edit/' . $user->id ); + + my $visible = $mech->visible_form_values; + is_deeply $visible, $test->{fields}, 'expected user'; + + my $expected = { + %{ $test->{fields} }, + %{ $test->{changes} } + }; + + $mech->submit_form_ok( { with_fields => $expected } ); + + # Some actions cause visible fields to be added/removed + foreach my $x (@{ $test->{removed} }) { + delete $expected->{$x}; + } + if ( $test->{added} ) { + $expected = { + %$expected, + %{ $test->{added} } + }; + } + + $visible = $mech->visible_form_values; + is_deeply $visible, $expected, 'user updated'; + + $mech->content_contains( 'Updated!' ); + }; + } +}; + +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'; + }; + +}; + +$user = $mech->create_user_ok('test@example.com', name => 'Test User'); + +subtest "Anonymizing user from admin" => sub { + $mech->create_problems_for_body(4, 2237, 'Title'); + my $count_p = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id })->count; + my $count_u = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id })->count; + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok({ button => 'anon_everywhere' }); + my $c = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id, anonymous => 1 })->count; + is $c, $count_p; + $c = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id, anonymous => 1 })->count; + is $c, $count_u; +}; + +subtest "Hiding user's reports from admin" => sub { + my $count_p = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id })->count; + my $count_u = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id })->count; + $mech->get_ok( '/admin/user_edit/' . $user->id ); + $mech->submit_form_ok({ button => 'hide_everywhere' }); + my $c = FixMyStreet::DB->resultset('Problem')->search({ user_id => $user->id, state => 'hidden' })->count; + is $c, $count_p; + $c = FixMyStreet::DB->resultset('Comment')->search({ user_id => $user->id, state => 'hidden' })->count; + is $c, $count_u; +}; + +done_testing(); |