use utf8; use CGI::Simple; use DateTime; use Test::MockModule; use FixMyStreet::TestMech; use Open311; use Open311::GetServiceRequests; use Open311::GetServiceRequestUpdates; use Open311::PostServiceRequestUpdates; use FixMyStreet::Script::Alerts; use FixMyStreet::Script::Reports; # disable info logs for this test run FixMyStreet::App->log->disable('info'); END { FixMyStreet::App->log->enable('info'); } my $cobrand = Test::MockModule->new('FixMyStreet::Cobrand::IsleOfWight'); $cobrand->mock('lookup_site_code', sub { my ($self, $row) = @_; return "Road ID" if $row->latitude == 50.7108; }); ok( my $mech = FixMyStreet::TestMech->new, 'Created mech object' ); my $params = { send_method => 'Open311', send_comments => 1, api_key => 'KEY', endpoint => 'endpoint', jurisdiction => 'home', can_be_devolved => 1, }; my $isleofwight = $mech->create_body_ok(2636, 'Isle of Wight Council', $params); my $contact = $mech->create_contact_ok( body_id => $isleofwight->id, category => 'Potholes', email => 'pothole@example.org', ); $contact->set_extra_fields( ( { code => 'urgent', datatype => 'string', description => 'question', variable => 'true', required => 'false', order => 1, datatype_description => 'datatype', } ) ); $contact->update; my $user = $mech->create_user_ok('user@example.org', name => 'Test User'); my $iow_user = $mech->create_user_ok('iow_user@example.org', name => 'IoW User', from_body => $isleofwight); $iow_user->user_body_permissions->create({ body => $isleofwight, permission_type => 'moderate', }); my $contact2 = $mech->create_contact_ok( body_id => $isleofwight->id, category => 'Roads', email => 'roads@example.org', send_method => 'Triage', ); my $admin_user = $mech->create_user_ok('admin-user@example.org', name => 'Admin User', from_body => $isleofwight); $admin_user->user_body_permissions->create({ body => $isleofwight, permission_type => 'triage' }); my @reports = $mech->create_problems_for_body(1, $isleofwight->id, 'An Isle of wight report', { confirmed => '2019-10-25 09:00', lastupdate => '2019-10-25 09:00', latitude => 50.7108, longitude => -1.29573, user => $user, external_id => 101202303 }); subtest "check clicking all reports link" => sub { FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => 'isleofwight', }, sub { $mech->get_ok('/'); $mech->follow_link_ok({ text => 'All reports' }); }; $mech->content_contains("An Isle of wight report", "Isle of Wight report there"); $mech->content_contains("Island Roads", "is still on cobrand"); }; subtest "use external id for reference number" => sub { FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => 'isleofwight', }, sub { $mech->get_ok('/report/' . $reports[0]->id); }; $mech->content_contains("101202303", "Display external id as reference number"); }; subtest "only original reporter can comment" => sub { FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => 'isleofwight', COBRAND_FEATURES => { updates_allowed => { isleofwight => 'reporter' } }, }, sub { $mech->get_ok('/report/' . $reports[0]->id); $mech->content_contains('Only the original reporter may leave updates'); $mech->log_in_ok('user@example.org'); $mech->get_ok('/report/' . $reports[0]->id); $mech->content_lacks('Only the original reporter may leave updates'); }; }; subtest "check moderation label uses correct name" => sub { my $REPORT_URL = '/report/' . $reports[0]->id; FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => ['isleofwight'], }, sub { $mech->log_out_ok; $mech->log_in_ok( $iow_user->email ); $mech->get_ok($REPORT_URL); $mech->content_lacks('show-moderation'); $mech->follow_link_ok({ text_regex => qr/^Moderate$/ }); $mech->content_contains('show-moderation'); $mech->submit_form_ok({ with_fields => { problem_title => 'Good good', problem_detail => 'Good good improved', }}); $mech->base_like( qr{\Q$REPORT_URL\E} ); $mech->content_like(qr/Moderated by Island Roads/); }; }; $_->delete for @reports; my $system_user = $mech->create_user_ok('system_user@example.org'); for my $status ( qw/ CLOSED FIXED DUPLICATE NOT_COUNCILS_RESPONSIBILITY NO_FURTHER_ACTION / ) { subtest "updates which mark report as $status close it to comments" => sub { my $dt = DateTime->now(formatter => DateTime::Format::W3CDTF->new)->add( minutes => -5 ); my ($p) = $mech->create_problems_for_body(1, $isleofwight->id, '', { lastupdate => $dt }); $p->update({ external_id => $p->id }); my $requests_xml = qq{ UPDATE_ID SERVICE_ID ID_EXTERNAL STATUS This is a note UPDATED_DATETIME }; my $update_dt = DateTime->now(formatter => DateTime::Format::W3CDTF->new); $requests_xml =~ s/STATUS/$status/; $requests_xml =~ s/UPDATE_ID/@{[$p->id]}/; $requests_xml =~ s/SERVICE_ID/@{[$p->id]}/; $requests_xml =~ s/ID_EXTERNAL/@{[$p->id]}/; $requests_xml =~ s/UPDATED_DATETIME/$update_dt/; my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'servicerequestupdates.xml' => $requests_xml } ); my $update = Open311::GetServiceRequestUpdates->new( system_user => $system_user, current_open311 => $o, current_body => $isleofwight, ); FixMyStreet::override_config { ALLOWED_COBRANDS => 'isleofwight', }, sub { $update->process_body; }; $mech->log_in_ok('user@example.org'); $mech->get_ok('/report/' . $p->id); $mech->content_lacks('Provide an update', "No update form on report"); $p->discard_changes; is $p->get_extra_metadata('closed_updates'), 1, "report closed to updates"; $p->comments->delete; $p->delete; }; } subtest "fetched requests do not use the description text" => sub { my $requests_xml = qq{ 638344 open This is a note. Potholes potholes\@example.org This the description of a pothole problem DATETIME DATETIME DATETIME 50.71086 -1.29573 }; my $dt = DateTime->now(formatter => DateTime::Format::W3CDTF->new)->add( minutes => -5 ); $requests_xml =~ s/DATETIME/$dt/gm; my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'requests.xml' => $requests_xml } ); my $update = Open311::GetServiceRequests->new( system_user => $iow_user, ); FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => 'isleofwight', }, sub { $update->create_problems( $o, $isleofwight ); }; my $p = FixMyStreet::DB->resultset('Problem')->search( { external_id => 638344 } )->first; ok $p, 'Found problem'; is $p->title, 'Potholes problem', 'correct problem title'; is $p->detail, 'Potholes problem', 'correct problem description'; $p->delete; }; subtest "fixing passes along the correct message" => sub { FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => 'isleofwight', }, sub { my $test_res = HTTP::Response->new(); $test_res->code(200); $test_res->message('OK'); $test_res->content('248'); my $o = Open311->new( fixmystreet_body => $isleofwight, test_mode => 1, test_get_returns => { 'servicerequestupdates.xml' => $test_res }, ); my ($p) = $mech->create_problems_for_body(1, $isleofwight->id, 'Title', { external_id => 1 }); my $c = FixMyStreet::DB->resultset('Comment')->create({ problem => $p, user => $p->user, anonymous => 't', text => 'Update text', problem_state => 'fixed - council', state => 'confirmed', mark_fixed => 0, confirmed => DateTime->now(), }); my $id = $o->post_service_request_update($c); is $id, 248, 'correct update ID returned'; my $cgi = CGI::Simple->new($o->test_req_used->content); like $cgi->param('description'), qr/^FMS-Update:/, 'FMS update prefix included'; unlike $cgi->param('description'), qr/The customer indicated that this issue had been fixed/, 'No fixed message included'; $c = $mech->create_comment_for_problem($p, $p->user, 'Name', 'Update text', 'f', 'confirmed', 'fixed - user'); $c->discard_changes; # Otherwise cannot set_nanosecond $id = $o->post_service_request_update($c); is $id, 248, 'correct update ID returned'; $cgi = CGI::Simple->new($o->test_req_used->content); like $cgi->param('description'), qr/^FMS-Update: \[The customer indicated that this issue had been fixed/, 'Fixed message included'; $p->comments->delete; $p->delete; }; }; subtest 'Check special Open311 request handling', sub { $mech->clear_emails_ok; my ($p) = $mech->create_problems_for_body(1, $isleofwight->id, 'Title', { category => 'Potholes', latitude => 50.7108, longitude => -1.29573, cobrand => 'isleofwight' }); $p->set_extra_fields({ name => 'urgent', value => 'no'}); $p->update; my $test_data; FixMyStreet::override_config { STAGING_FLAGS => { send_reports => 1 }, ALLOWED_COBRANDS => ['isleofwight' ], MAPIT_URL => 'http://mapit.uk/', }, sub { $test_data = FixMyStreet::Script::Reports::send(); }; $p->discard_changes; ok $p->whensent, 'Report marked as sent'; is $p->send_method_used, 'Open311', 'Report sent via Open311'; is $p->external_id, 248, 'Report has right external ID'; my $req = $test_data->{test_req_used}; my $c = CGI::Simple->new($req->content); is $c->param('attribute[urgent]'), undef, 'no urgent param sent'; is $c->param('attribute[site_code]'), 'Road ID', 'road ID set'; $mech->email_count_is(1); my $email = $mech->get_email; ok $email, "got an email"; like $mech->get_text_body_from_email($email), qr/your enquiry has been received by Island Roads/, "correct report send email text"; }; subtest "comment recording triage details is not sent" => sub { FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => [ 'isleofwight' ], }, sub { my $test_res = HTTP::Response->new(); $test_res->code(200); $test_res->message('OK'); $test_res->content(''); my $o = Open311->new( fixmystreet_body => $isleofwight, test_mode => 1, test_get_returns => { 'servicerequestupdates.xml' => $test_res }, ); my ($p) = $mech->create_problems_for_body( 1, $isleofwight->id, 'Title', { category => 'Roads', areas => 2636, latitude => 50.71086, longitude => -1.29573, whensent => DateTime->now->add( minutes => -5 ), send_method_used => 'Triage', state => 'for triage', external_id => 1, }); $mech->log_out_ok; $mech->log_in_ok($admin_user->email); my $report_url = '/report/' . $p->id; $mech->get_ok($report_url); $mech->submit_form_ok( { with_fields => { category => 'Potholes', include_update => 0, } }, 'triage form submitted' ); ok $p->comments->first, 'comment created for problem'; $p->update({ send_method_used => 'Open311', whensent => DateTime->now->add( minutes => -5 ), }); my $updates = Open311::PostServiceRequestUpdates->new( current_open311 => $o, ); my $id = $updates->process_body($isleofwight); ok !$o->test_req_used, 'no open311 update sent'; $p->comments->delete; $p->delete; }; }; my ($p) = $mech->create_problems_for_body(1, $isleofwight->id, '', { cobrand => 'isleofwight' }); my $alert = FixMyStreet::DB->resultset('Alert')->create( { parameter => $p->id, alert_type => 'new_updates', user => $user, cobrand => 'isleofwight', } )->confirm; subtest "sends branded alert emails" => sub { $mech->create_comment_for_problem($p, $system_user, 'Other User', 'This is some update text', 'f', 'confirmed', undef); $mech->clear_emails_ok; FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => ['isleofwight','fixmystreet'], }, sub { FixMyStreet::Script::Alerts::send(); }; $mech->email_count_is(1); my $email = $mech->get_email; ok $email, "got an email"; like $mech->get_text_body_from_email($email), qr/Island Roads/, "emails are branded"; }; $p->comments->delete; $p->delete; subtest "check not responsible as correct text" => sub { my ($p) = $mech->create_problems_for_body( 1, $isleofwight->id, 'Title', { category => 'Roads', areas => 2636, latitude => 50.71086, longitude => -1.29573, whensent => DateTime->now->add( minutes => -5 ), send_method_used => 'Open311', state => 'not responsible', external_id => 1, }); my $c = FixMyStreet::DB->resultset('Comment')->create({ problem => $p, user => $p->user, anonymous => 't', text => 'Update text', problem_state => 'not responsible', state => 'confirmed', mark_fixed => 0, confirmed => DateTime->now(), }); FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => ['isleofwight'], }, sub { $mech->get_ok('/report/' . $p->id); }; $mech->content_contains("not Island Roads’ responsibility", "not reponsible message contains correct text"); $p->comments->delete; $p->delete; }; subtest "sends branded confirmation emails" => sub { $mech->log_out_ok; $mech->clear_emails_ok; $mech->get_ok('/around'); FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'isleofwight' ], MAPIT_URL => 'http://mapit.uk/', }, sub { $mech->submit_form_ok( { with_fields => { pc => 'PO30 5XJ', } }, "submit location" ); # click through to the report page $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); $mech->submit_form_ok( { button => 'submit_register', with_fields => { title => 'Test Report', detail => 'Test report details.', photo1 => '', name => 'Joe Bloggs', username => 'test-1@example.com', category => 'Roads', } }, "submit good details" ); $mech->email_count_is(1); my $email = $mech->get_email; ok $email, "got an email"; like $mech->get_text_body_from_email($email), qr/Island Roads/, "emails are branded"; my $url = $mech->get_link_from_email($email); $mech->get_ok($url); $mech->clear_emails_ok; }; }; subtest "sends branded report sent emails" => sub { $mech->clear_emails_ok; FixMyStreet::override_config { STAGING_FLAGS => { send_reports => 1 }, MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => ['isleofwight','fixmystreet'], }, sub { FixMyStreet::Script::Reports::send(); }; $mech->email_count_is(1); my $email = $mech->get_email; ok $email, "got an email"; like $mech->get_text_body_from_email($email), qr/Island Roads/, "emails are branded"; }; subtest "check category extra uses correct name" => sub { my @extras = ( { code => 'test', datatype => 'string', description => 'question', variable => 'true', required => 'false', order => 1, datatype_description => 'datatype', } ); $contact2->set_extra_fields( @extras ); $contact2->update; my $extra_details; FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => ['isleofwight','fixmystreet'], }, sub { $extra_details = $mech->get_ok_json('/report/new/category_extras?category=Roads&latitude=50.71086&longitude=-1.29573'); }; like $extra_details->{category_extra}, qr/Island Roads/, 'correct name in category extras'; }; subtest "reports are marked for triage upon submission" => sub { $mech->log_out_ok; $mech->clear_emails_ok; $mech->log_in_ok($user->email); $mech->get_ok('/around'); FixMyStreet::override_config { STAGING_FLAGS => { send_reports => 1, skip_checks => 0 }, ALLOWED_COBRANDS => [ 'isleofwight' ], MAPIT_URL => 'http://mapit.uk/', }, sub { $mech->submit_form_ok( { with_fields => { pc => 'PO30 5XJ', } }, "submit location" ); # click through to the report page $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); $mech->submit_form_ok( { button => 'submit_register', with_fields => { title => 'Test Report', detail => 'Test report details.', photo1 => '', category => 'Roads', } }, "submit good details" ); my $report = $user->problems->first; ok $report, "Found the report"; is $report->state, 'confirmed', 'report confirmed'; $mech->clear_emails_ok; FixMyStreet::Script::Reports::send(); $report->discard_changes; is $report->state, 'for triage', 'report marked as for triage'; ok $report->whensent, 'report marked as sent'; $mech->email_count_is(1); my $email = $mech->get_email; like $mech->get_text_body_from_email($email), qr/submitted to Island Roads for review/, 'correct text for email sent for Triage'; }; }; for my $cobrand ( 'fixmystreet', 'isleofwight' ) { subtest "only categories for Triage are displayed on " . $cobrand => sub { $mech->log_out_ok; $mech->get_ok('/around'); FixMyStreet::override_config { ALLOWED_COBRANDS => [ $cobrand ], MAPIT_URL => 'http://mapit.uk/', }, sub { $mech->submit_form_ok( { with_fields => { pc => 'PO30 5XJ', } }, "submit location" ); # click through to the report page $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); my $f = $mech->form_name('mapSkippedForm'); ok $f, 'found form'; my $cats = $f->find_input('category'); ok $cats, 'found category element'; my @values = $cats->possible_values; is_deeply \@values, [ '-- Pick a category --', 'Roads' ], 'correct category list'; }; }; subtest "staff user can see non Triage categories on " . $cobrand => sub { $mech->log_out_ok; $mech->log_in_ok($admin_user->email); $mech->get_ok('/around'); FixMyStreet::override_config { ALLOWED_COBRANDS => [ $cobrand ], MAPIT_URL => 'http://mapit.uk/', }, sub { $mech->submit_form_ok( { with_fields => { pc => 'PO30 5XJ', } }, "submit location" ); # click through to the report page $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); my $f = $mech->form_name('mapSkippedForm'); ok $f, 'found form'; my $cats = $f->find_input('category'); ok $cats, 'found category element'; my @values = $cats->possible_values; is_deeply \@values, [ '-- Pick a category --', 'Potholes' ], 'correct category list'; }; }; } done_testing();