aboutsummaryrefslogtreecommitdiffstats
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/00-check-config.t51
-rw-r--r--t/00-check-we-are-staging.t24
-rwxr-xr-xt/Cobrand.t282
-rw-r--r--t/Cobrands/.gitignore1
-rw-r--r--t/Cobrands/Mysite/Util.pm109
-rwxr-xr-xt/Page.t107
-rwxr-xr-xt/Problems.t22
-rw-r--r--t/app/01app.t10
-rw-r--r--t/app/02pod.t10
-rw-r--r--t/app/03podcoverage.t14
-rw-r--r--t/app/controller/about.t31
-rw-r--r--t/app/controller/admin.t729
-rw-r--r--t/app/controller/alert.t66
-rw-r--r--t/app/controller/alert_new.t470
-rw-r--r--t/app/controller/around.t81
-rw-r--r--t/app/controller/auth.t227
-rw-r--r--t/app/controller/contact.t275
-rw-r--r--t/app/controller/council.t14
-rw-r--r--t/app/controller/index.t58
-rw-r--r--t/app/controller/json.t119
-rw-r--r--t/app/controller/my.t19
-rw-r--r--t/app/controller/page_not_found.t20
-rw-r--r--t/app/controller/questionnaire.t293
-rw-r--r--t/app/controller/report_display.t265
-rw-r--r--t/app/controller/report_import.t332
-rw-r--r--t/app/controller/report_new.t647
-rw-r--r--t/app/controller/report_updates.t1030
-rw-r--r--t/app/controller/reports.t37
-rw-r--r--t/app/controller/sample.jpgbin0 -> 22588 bytes
-rw-r--r--t/app/helpers/send_email.t56
-rw-r--r--t/app/helpers/send_email_sample.txt29
-rw-r--r--t/app/load_general_config.t13
-rw-r--r--t/app/model/comment.t27
-rw-r--r--t/app/model/db.t8
-rw-r--r--t/app/model/problem.t155
-rw-r--r--t/app/model/token.t96
-rw-r--r--t/app/uri_for.t108
-rw-r--r--t/app/view/email.t8
-rw-r--r--t/app/view/web.t8
-rw-r--r--t/cobrand/loading.t71
-rw-r--r--t/fixmystreet.t37
-rw-r--r--t/i18n.t121
-rw-r--r--t/templates/mysite/test-header1
43 files changed, 5559 insertions, 522 deletions
diff --git a/t/00-check-config.t b/t/00-check-config.t
new file mode 100644
index 000000000..7d334152c
--- /dev/null
+++ b/t/00-check-config.t
@@ -0,0 +1,51 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet;
+
+# check that all the fields listed in general-example are also present in
+# general - helps prevent later test failures due to un-noticed additions to the
+# config file.
+
+# This code will bail_out to prevent the test suite proceeding to save time if
+# issues are found.
+
+# load the config file and store the contents in a readonly hash
+
+mySociety::Config::set_file( FixMyStreet->path_to("conf/general-example") );
+my $example_config = mySociety::Config::get_list();
+mySociety::Config::set_file( FixMyStreet->path_to("conf/general") );
+my $local_config = mySociety::Config::get_list();
+
+# find all keys missing from each config
+my @missing_from_example = find_missing( $example_config, $local_config );
+my @missing_from_local = find_missing( $local_config, $example_config );
+
+if ( @missing_from_example || @missing_from_local ) {
+
+ fail "Missing from 'general': $_" for @missing_from_local;
+ fail "Missing from 'general-example': $_" for @missing_from_example;
+
+ # bail out to prevent other tests failing due to config issues
+ BAIL_OUT( "Config has changed"
+ . " - update your 'general' and add/remove the keys listed above" );
+}
+else {
+ pass "configs contain the same keys";
+}
+
+done_testing();
+
+sub find_missing {
+ my $reference = shift;
+ my $config = shift;
+ my @missing = ();
+
+ foreach my $key ( sort keys %$config ) {
+ push @missing, $key unless exists $reference->{$key};
+ }
+
+ return @missing;
+}
diff --git a/t/00-check-we-are-staging.t b/t/00-check-we-are-staging.t
new file mode 100644
index 000000000..a1da68798
--- /dev/null
+++ b/t/00-check-we-are-staging.t
@@ -0,0 +1,24 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet;
+
+# check that all the fields listed in general-example are also present in
+# general - helps prevent later test failures due to un-noticed additions to the
+# config file.
+
+# This code will bail_out to prevent the test suite proceeding to save time if
+# issues are found.
+
+# load the config file and store the contents in a readonly hash
+
+mySociety::Config::set_file( FixMyStreet->path_to("conf/general") );
+
+BAIL_OUT( "Test suite modifies databases so should not be run on live servers" )
+ unless mySociety::Config::get('STAGING_SITE', undef);
+
+ok mySociety::Config::get('STAGING_SITE', undef), 'staging server';
+
+done_testing();
diff --git a/t/Cobrand.t b/t/Cobrand.t
deleted file mode 100755
index 1f38ec605..000000000
--- a/t/Cobrand.t
+++ /dev/null
@@ -1,282 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Cobrand.t:
-# Tests for the cobranding functions
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Cobrand.t,v 1.25 2009-12-16 12:43:12 matthew Exp $
-#
-
-use strict;
-use warnings;
-use Test::More tests => 59;
-use Test::Exception;
-use Error qw(:try);
-
-use FindBin;
-use lib "$FindBin::Bin";
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-
-use Cobrand;
-use mySociety::MockQuery;
-
-sub test_site_restriction {
- my ($site_restriction, $site_id) = Cobrand::site_restriction('mysite', 'test');
- like($site_restriction, qr/ and council = 1 /, 'should return result of cobrand module site_restriction function');
- ok($site_id == 99, 'should return result of cobrand module site_restriction function');
-
- ($site_restriction, $site_id) = Cobrand::site_restriction('nosite', 'test');
- ok($site_restriction eq '', 'should return "" and zero if no module exists' );
- ok($site_id == 0, 'should return "" and zero if no module exists');
-}
-
-sub test_form_elements {
- my $q = new MockQuery('mysite');
- my $element_html = Cobrand::form_elements('mysite', 'postcodeForm', $q);
- ok($element_html eq 'Extra html', 'should return result of cobrand module element_html function') or diag("Got $element_html");
-
- $element_html = Cobrand::form_elements('nosite', 'postcodeForm', $q);
- ok($element_html eq '', 'should return an empty string if no cobrand module exists') or diag("Got $element_html");
-}
-
-sub test_disambiguate_location {
- my $q = new MockQuery('mysite');
- my $s = 'London Road';
- $s = Cobrand::disambiguate_location('mysite', $s, $q);
- ok($s eq 'Specific Location', 'should return result of cobrand module disambiguate_location function') or diag("Got $s");;
-
- $q = new MockQuery('nosite');
- $s = 'London Road';
- $s = Cobrand::disambiguate_location('nosite', $s, $q);
- ok($s eq 'London Road&gl=uk', 'should return location string as passed if no cobrand module exists') or diag("Got $s");
-
-}
-
-sub test_cobrand_handle {
- my $cobrand = 'mysite';
- my $handle = Cobrand::cobrand_handle($cobrand);
- like($handle->site_name(), qr/mysite/, 'should get a module handle if Util module exists for cobrand');
- $cobrand = 'nosite';
- $handle = Cobrand::cobrand_handle($cobrand);
- ok($handle == 0, 'should return zero if no module exists');
-}
-
-sub test_extra_problem_data {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the result of the page function in the cobrand module if one exists
- my $cobrand_data = Cobrand::extra_problem_data($cobrand, $q);
- ok($cobrand_data eq 'Cobrand problem data', 'extra_problem_data should return data from cobrand module') or diag("Got $cobrand_data");
-
- # should return an empty string if no cobrand module exists
- $q = new MockQuery('nosite');
- $cobrand_data = Cobrand::extra_problem_data('nosite', $q);
- ok($cobrand_data eq '', 'extra_problem_data should return an empty string if there is no cobrand module') or diag("Got $cobrand_data");
-}
-
-sub test_extra_update_data {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the result of the page function in the cobrand module if one exists
- my $cobrand_data = Cobrand::extra_update_data($cobrand, $q);
- ok($cobrand_data eq 'Cobrand update data', 'extra_update_data should return data from cobrand module') or diag("Got $cobrand_data");
-
- # should return an empty string if no cobrand module exists
- $q = new MockQuery('nosite');
- $cobrand_data = Cobrand::extra_update_data('nosite', $q);
- ok($cobrand_data eq '', 'extra_update_data should return an empty string if there is no cobrand module') or diag("Got $cobrand_data");
-}
-
-
-sub test_extra_alert_data {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the result of the page function in the cobrand module if one exists
- my $cobrand_data = Cobrand::extra_alert_data($cobrand, $q);
- ok($cobrand_data eq 'Cobrand alert data', 'extra_alert_data should return data from cobrand module') or diag("Got $cobrand_data");
-
- # should return an empty string if no cobrand module exists
- $q = new MockQuery('nosite');
- $cobrand_data = Cobrand::extra_alert_data('nosite', $q);
- ok($cobrand_data eq '', 'extra_alert_data should return an empty string if there is no cobrand module') or diag("Got $cobrand_data");
-}
-
-sub test_base_url {
- my $cobrand = 'mysite';
-
- # should get the result of the page function in the cobrand module if one exists
- my $base_url = Cobrand::base_url($cobrand);
- is('http://mysite.example.com', $base_url, 'base_url returns output from cobrand module');
-
- # should return the base url from the config if there is no cobrand module
- $cobrand = 'nosite';
- $base_url = Cobrand::base_url($cobrand);
- is(mySociety::Config::get('BASE_URL'), $base_url, 'base_url returns config base url if no cobrand module');
-
-}
-
-sub test_base_url_for_emails {
- my $cobrand = 'mysite';
-
- # should get the results of the base_url_for_emails function in the cobrand module if one exists
- my $base_url = Cobrand::base_url_for_emails($cobrand);
- is('http://mysite.foremails.example.com', $base_url, 'base_url_for_emails returns output from cobrand module') ;
-
- # should return the result of Cobrand::base_url otherwise
- $cobrand = 'nosite';
- $base_url = Cobrand::base_url_for_emails($cobrand);
- is(mySociety::Config::get('BASE_URL'), $base_url, 'base_url_for_emails returns config base url if no cobrand module');
-
-}
-
-sub test_extra_params {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the results of the extra_params function in the cobrand module if one exists
- my $extra_params = Cobrand::extra_params($cobrand, $q);
- is($extra_params, 'key=value', 'extra_params returns output from cobrand module') ;
-
- # should return an empty string otherwise
- $cobrand = 'nosite';
- $extra_params = Cobrand::extra_params($cobrand, $q);
- is($extra_params, '', 'extra_params returns an empty string if no cobrand module');
-
-}
-
-sub test_header_params {
- my $cobrand = 'mysite';
- my $q = new MockQuery($cobrand);
-
- # should get the results of the header_params function in the cobrand module if one exists
- my $header_params = Cobrand::header_params($cobrand, $q);
- is_deeply($header_params, {'key' => 'value'}, 'header_params returns output from cobrand module') ;
-
- # should return an empty string otherwise
- $cobrand = 'nosite';
- $header_params = Cobrand::header_params($cobrand, $q);
- is_deeply($header_params, {}, 'header_params returns an empty hash ref if no cobrand module');
-}
-
-sub test_root_path_js {
- my $cobrand = 'mysite';
- my $root_path_js = Cobrand::root_path_js($cobrand);
-
- # should get the results of the root_path_js function in the cobrand module if one exists
- is($root_path_js, 'root path js', 'root_path_js returns output from cobrand module');
-
- # should return a js string setting the root path to an empty string otherwise
- $cobrand = 'nosite';
- $root_path_js = Cobrand::root_path_js($cobrand);
- is($root_path_js, 'var root_path = "";', 'root_path_pattern returns a string setting the root path to an empty string if no cobrand module');
-}
-
-sub test_site_title {
- my $cobrand = 'mysite';
- my $site_title = Cobrand::site_title($cobrand);
-
- # should get the results of the site_title function in the cobrand module if one exists
- is($site_title, 'Mysite Title', 'site_title returns output from cobrand module');
-
- # should return an empty string otherwise
- $cobrand = 'nosite';
- $site_title = Cobrand::site_title($cobrand);
- is($site_title, '', 'site_title returns an empty string if no site title');
-}
-
-sub test_on_map_list_limit {
- my $cobrand = 'mysite';
- my $limit = Cobrand::on_map_list_limit($cobrand);
-
- is($limit, 30, 'on_map_list_limit returns output from cobrand module');
-
- $cobrand = 'nosite';
- $limit = Cobrand::on_map_list_limit($cobrand);
- is($limit, undef, 'on_map_list_limit returns undef if there is no limit defined by the cobrand');
-
-}
-
-sub test_url {
- my $cobrand = 'mysite';
- my $q = new MockQuery('mysite');
- my $url = Cobrand::url($cobrand, '/xyz', $q);
- is($url, '/transformed_url', 'url returns output from cobrand module');
-
- $cobrand = 'nosite';
- $url = Cobrand::url($cobrand, '/xyz', $q);
- is($url, '/xyz', 'url returns passed url if there is no url function defined by the cobrand');
-}
-
-sub test_allow_photo_upload {
- my $cobrand = 'mysite';
- my $photo_upload = Cobrand::allow_photo_upload($cobrand);
- is($photo_upload, 0, 'allow_photo_upload returns output from cobrand module');
-
- $cobrand = 'nosite';
- $photo_upload = Cobrand::allow_photo_upload($cobrand);
- is($photo_upload, 1, 'allow_photo_upload returns 1 if there is no allow_photo_upload function defined by the cobrand');
-}
-
-sub test_allow_photo_display {
- my $cobrand = 'mysite';
- my $photo_display = Cobrand::allow_photo_display($cobrand);
- is($photo_display, 0, 'allow_photo_display returns output from cobrand module');
-
- $cobrand = 'nosite';
- $photo_display = Cobrand::allow_photo_display($cobrand);
- is($photo_display, 1, 'allow_photo_display returns 1 if there is no allow_photo_display function defined by the cobrand');
-}
-
-sub test_council_check {
- my $cobrand = 'mysite';
- my $councils = {};
- my $query = new MockQuery('mysite');
- my ($check_result, $error) = Cobrand::council_check($cobrand, $councils, $query);
- is($check_result, 0, 'council_check returns output from cobrand module');
-
- $cobrand = 'nosite';
- ($check_result, $error) = Cobrand::council_check($cobrand, $councils, $query);
- is($check_result, 1, 'council_check returns 1 if there is no council_check function defined by the cobrand');
-}
-
-sub test_recent {
- my $cobrand = 'mysite';
- my $check_result = Cobrand::recent($cobrand);
- is_deeply($check_result, [ { id => 1, title => 'Title 1' }, { id => 2, title => 'Title 2' } ], 'recent returns output from cobrand module');
-
- # Can't test default here as calls database. So test it throws a db not configured error :)
- my $error;
- $cobrand = 'nosite';
- try {
- $check_result = Cobrand::recent($cobrand);
- } catch Error with {
- $error = shift;
- };
- ok($error =~ /^configure not yet called in new_dbh/, 'Default throws a database error');
-}
-
-ok(test_cobrand_handle() == 1, 'Ran all tests for the cobrand_handle function');
-ok(test_site_restriction() == 1, 'Ran all tests for the site_restriction function');
-ok(test_base_url() == 1, 'Ran all tests for the base url');
-ok(test_disambiguate_location() == 1, 'Ran all tests for disambiguate location');
-ok(test_form_elements() == 1, 'Ran all tests for form_elements');
-ok(test_base_url_for_emails() == 1, 'Ran all tests for base_url_for_emails');
-ok(test_extra_problem_data() == 1, 'Ran all tests for extra_problem_data');
-ok(test_extra_update_data() == 1, 'Ran all tests for extra_update_data');
-ok(test_extra_alert_data() == 1, 'Ran all tests for extra_alert_data');
-ok(test_extra_params() == 1, 'Ran all tests for extra_params');
-ok(test_header_params() == 1, 'Ran all tests for header_params');
-ok(test_root_path_js() == 1, 'Ran all tests for root_js');
-ok(test_site_title() == 1, 'Ran all tests for site_title');
-ok(test_on_map_list_limit() == 1, 'Ran all tests for on_map_list_limit');
-ok(test_url() == 1, 'Ran all tests for url');
-ok(test_allow_photo_upload() == 1, 'Ran all tests for allow_photo_upload');
-ok(test_allow_photo_display() == 1, 'Ran all tests for allow_photo_display');
-ok(test_council_check() == 1, 'Ran all tests for council_check');
-ok(test_recent() == 1, 'Ran all tests for recent');
diff --git a/t/Cobrands/.gitignore b/t/Cobrands/.gitignore
deleted file mode 100644
index 77650dd46..000000000
--- a/t/Cobrands/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/Cities \ No newline at end of file
diff --git a/t/Cobrands/Mysite/Util.pm b/t/Cobrands/Mysite/Util.pm
deleted file mode 100644
index c1a8023c1..000000000
--- a/t/Cobrands/Mysite/Util.pm
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Util.pm:
-# Test Cobranding for FixMyStreet.
-#
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: Util.pm,v 1.20 2009-12-16 12:43:13 matthew Exp $
-
-package Cobrands::Mysite::Util;
-use Page;
-use strict;
-use Carp;
-use mySociety::Web qw(ent);
-
-sub new {
- my $class = shift;
- return bless {}, $class;
-}
-
-sub site_name {
- return 'mysite';
-}
-
-sub site_restriction {
- return (' and council = 1 ', 99);
-}
-
-sub page {
- my %params = ();
- return ("A cobrand produced page", %params);
-}
-
-sub base_url {
- return 'http://mysite.example.com';
-}
-
-sub base_url_for_emails {
- return 'http://mysite.foremails.example.com';
-}
-
-sub disambiguate_location {
- return 'Specific Location';
-}
-
-sub form_elements {
- return "Extra html";
-}
-
-sub extra_problem_data {
- return "Cobrand problem data";
-}
-
-sub extra_update_data {
- return "Cobrand update data";
-}
-
-sub extra_alert_data {
- return "Cobrand alert data";
-}
-
-sub extra_params {
- return 'key=value';
-}
-
-sub header_params {
- my %params = ('key' => 'value');
- return \%params;
-}
-
-
-sub root_path_js {
- return 'root path js';
-}
-
-sub site_title {
- return 'Mysite Title';
-}
-
-sub on_map_list_limit {
- return 30;
-}
-
-sub url {
- return '/transformed_url';
-}
-
-sub allow_photo_upload {
- return 0;
-}
-
-sub allow_photo_display {
- return 0;
-}
-
-sub council_check {
- return 0;
-}
-
-sub recent {
- return [
- { id => 1, title => 'Title 1' },
- { id => 2, title => 'Title 2' }
- ];
-}
-
-1;
diff --git a/t/Page.t b/t/Page.t
deleted file mode 100755
index a1ae7f3aa..000000000
--- a/t/Page.t
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Page.t:
-# Tests for the Page functions
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Page.t,v 1.12 2009-12-09 13:34:36 louise Exp $
-#
-
-use strict;
-use warnings;
-use Test::More tests => 13;
-use Test::Exception;
-
-use FindBin;
-use lib "$FindBin::Bin";
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-
-use Page;
-use FixMyStreet::Geocode;
-use mySociety::MockQuery;
-use mySociety::Locale;
-
-sub mock_query(){
- my $q = new MockQuery('mysite');
- return $q;
-}
-
-sub set_lang($) {
- my $lang = shift;
- mySociety::Locale::negotiate_language($lang);
- mySociety::Locale::gettext_domain('FixMyStreet');
- mySociety::Locale::change();
-}
-
-sub test_geocode_string() {
- my %params = ();
- my $q = new MockQuery( 'nosite', \%params );
-
- # geocode a straightforward string, expect success
- my ( $latitude, $longitude, $error ) =
- FixMyStreet::Geocode::string( 'Buckingham Palace', $q );
- is( $latitude, 51.5013639, 'example easting generated' );
- is( $longitude, -0.1418898, 'example northing generated' );
- is( $error, undef, 'should not generate error for simple example' );
-
- # expect a failure message for Northern Ireland
- ( $latitude, $longitude, $error ) =
- FixMyStreet::Geocode::string( 'Falls Road, Belfast', $q );
- is(
- $error,
- "We do not cover Northern Ireland, I'm afraid, as our licence doesn't "
- . "include any maps for the region.",
- 'error message produced for NI location'
- );
-}
-
-sub test_header() {
- my $q = mock_query();
- my $html;
- my %params = (title => 'test title');
- set_lang('en-gb,English,en_GB');
-
- # Test that param that isn't explicitly allowed raises error
- $params{'test-param'} = 'test';
- throws_ok { Page::header($q, %params); } qr/bad parameter/, 'bad parameter caught ok';
- delete $params{'test-param'};
-
- # Test that template passed is rendered
- $params{'template'} = 'test';
- $html = Page::template_include('test-header', $q,
- '/../t/templates/' . $q->{site} . '/',
- title => 'My test title', lang => 'en-gb'
- );
-
- like ($html, qr/My test header template/, 'named template rendered ok');
-
- return 1;
-}
-
-sub test_footer(){
- return 1;
-}
-
-sub test_base_url_with_lang {
- set_lang('en-gb,English,en_GB');
- my $q = mock_query();
- my $url = Page::base_url_with_lang($q);
- ok($url eq 'http://mysite.example.com', 'Basic url rendered ok');
-
- $q = new MockQuery('emptyhomes');
- $url = Page::base_url_with_lang($q);
- like($url, qr/http:\/\/en\.emptyhomes\./, 'Empty homes url with lang returned ok');
-
- $url = Page::base_url_with_lang($q, 1);
- like($url, qr/http:\/\/cy\.emptyhomes\./, 'Empty homes url with lang reversed returned ok');
-
-}
-
-
-ok(test_base_url_with_lang() == 1, 'Ran all tests for base_url_with_lang');
-ok(test_footer() == 1, 'Ran all tests for the footer function');
-ok(test_header() == 1, 'Ran all tests for the header function');
-ok(test_geocode_string() == 1, 'Ran all tests for the geocode_string function');
diff --git a/t/Problems.t b/t/Problems.t
deleted file mode 100755
index 40b3b810c..000000000
--- a/t/Problems.t
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Problem.t:
-# Tests for the Problem functions
-#
-# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
-# Email: louise@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: Problems.t,v 1.3 2009-11-12 14:05:18 louise Exp $
-#
-
-use strict;
-use warnings;
-use Test::More tests => 1;
-use Test::Exception;
-
-use FindBin;
-use lib "$FindBin::Bin";
-use lib "$FindBin::Bin/../perllib";
-use lib "$FindBin::Bin/../commonlib/perllib";
-BEGIN { use_ok( 'Problems' ); }
-
diff --git a/t/app/01app.t b/t/app/01app.t
new file mode 100644
index 000000000..02ffcd217
--- /dev/null
+++ b/t/app/01app.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'FixMyStreet::App';
+
+ok( request('/')->is_success, 'Request should succeed' );
+
+done_testing();
diff --git a/t/app/02pod.t b/t/app/02pod.t
new file mode 100644
index 000000000..ababc2eaa
--- /dev/null
+++ b/t/app/02pod.t
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();
diff --git a/t/app/03podcoverage.t b/t/app/03podcoverage.t
new file mode 100644
index 000000000..6ddc5c6b6
--- /dev/null
+++ b/t/app/03podcoverage.t
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();
diff --git a/t/app/controller/about.t b/t/app/controller/about.t
new file mode 100644
index 000000000..a5fb215d3
--- /dev/null
+++ b/t/app/controller/about.t
@@ -0,0 +1,31 @@
+use strict;
+use warnings;
+
+use Test::More;
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+
+ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' );
+
+# check that we can get the page
+$mech->get_ok('/about');
+$mech->content_like(qr{About us ::\s+FixMyStreet});
+$mech->content_contains('html lang="en-gb"');
+
+SKIP: {
+ skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 8 )
+ unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{emptyhomes};
+
+ # check that geting the page as EHA produces a different page
+ ok $mech->host("reportemptyhomes.co.uk"), 'change host to reportemptyhomes';
+ $mech->get_ok('/about');
+ $mech->content_like(qr{About us ::\s+Report Empty Homes});
+ $mech->content_contains('html lang="en-gb"');
+
+ # check that geting the page as EHA in welsh produces a different page
+ ok $mech->host("cy.reportemptyhomes.co.uk"), 'host to cy.reportemptyhomes';
+ $mech->get_ok('/about');
+ $mech->content_like(qr{Amdanom ni ::\s+Adrodd am Eiddo Gwag});
+ $mech->content_contains('html lang="cy"');
+}
+
+done_testing();
diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t
new file mode 100644
index 000000000..7f8590747
--- /dev/null
+++ b/t/app/controller/admin.t
@@ -0,0 +1,729 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+my $secret = FixMyStreet::App->model('DB::Secret')->search();
+
+# don't explode if there's nothing in the secret table
+if ( $secret == 0 ) {
+ diag "You need to put an entry in the secret table for the admin tests to run";
+ plan skip_all => 'No entry in secret table';
+}
+
+my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test@example.com', name => 'Test User' } );
+ok $user, "created test user";
+
+my $user2 =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test2@example.com', name => 'Test User 2' } );
+ok $user2, "created second test user";
+
+
+my $user3 =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test3@example.com', name => 'Test User 2' } );
+
+if ( $user3 ) {
+ $mech->delete_user( $user3 );
+}
+
+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',
+ council => '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',
+ 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 $alert = FixMyStreet::App->model('DB::Alert')->find_or_create(
+ {
+ alert_type => 'new_updates',
+ parameter => $report->id,
+ confirmed => 1,
+ user => $user,
+ },
+);
+
+subtest 'check summary counts' => sub {
+ my $problems = FixMyStreet::App->model('DB::Problem')->search( { state => { -in => [qw/confirmed fixed/] } } );
+
+ my $problem_count = $problems->count;
+ $problems->update( { cobrand => '' } );
+
+ my $q = FixMyStreet::App->model('DB::Questionnaire')->find_or_new( { problem => $report, });
+ $q->whensent( \'ms_current_timestamp()' );
+ $q->in_storage ? $q->update : $q->insert;
+
+ my $alerts = FixMyStreet::App->model('DB::Alert')->search( { confirmed => { '>' => 0 } } );
+ my $a_count = $alerts->count;
+
+ $mech->get_ok('/admin');
+
+ $mech->title_like(qr/Summary/);
+
+ $mech->content_contains( "$problem_count</strong> live problems" );
+ $mech->content_contains( "$a_count confirmed alerts" );
+
+ my $questionnaires = FixMyStreet::App->model('DB::Questionnaire')->search( { whensent => { -not => undef } } );
+ my $q_count = $questionnaires->count();
+
+ $mech->content_contains( "$q_count questionnaires sent" );
+
+ ok $mech->host('barnet.fixmystreet.com');
+
+ $mech->get_ok('/admin');
+ $mech->title_like(qr/Summary/);
+
+ my ($num_live) = $mech->content =~ /(\d+)<\/strong> live problems/;
+ my ($num_alerts) = $mech->content =~ /(\d+) confirmed alerts/;
+ my ($num_qs) = $mech->content =~ /(\d+) questionnaires sent/;
+
+ $report->council(2489);
+ $report->cobrand('barnet');
+ $report->update;
+
+ $alert->cobrand('barnet');
+ $alert->update;
+
+ $mech->get_ok('/admin');
+
+ $mech->content_contains( ($num_live+1) . "</strong> live problems" );
+ $mech->content_contains( ($num_alerts+1) . " confirmed alerts" );
+ $mech->content_contains( ($num_qs+1) . " questionnaires sent" );
+
+ $report->council(2504);
+ $report->cobrand('');
+ $report->update;
+
+ $alert->cobrand('');
+ $alert->update;
+
+ ok $mech->host('fixmystreet.com');
+};
+
+my $host = FixMyStreet->config('BASE_URL');
+$mech->get_ok('/admin/council_contacts/2650');
+$mech->content_contains('Aberdeen City Council');
+$mech->content_contains('AB15 8RN');
+$mech->content_contains("$host/around");
+
+subtest 'check contact creation' => sub {
+ my $contact = FixMyStreet::App->model('DB::Contact')->search(
+ { area_id => 2650, category => [ 'test category', 'test/category' ] }
+ );
+ $contact->delete_all;
+
+ my $history = FixMyStreet::App->model('DB::ContactsHistory')->search(
+ { area_id => 2650, category => [ 'test category', 'test/category' ] }
+ );
+ $history->delete_all;
+
+ $mech->get_ok('/admin/council_contacts/2650');
+
+ $mech->submit_form_ok( { with_fields => {
+ category => 'test category',
+ email => 'test@example.com',
+ note => 'test note',
+ } } );
+
+ $mech->content_contains( 'test category' );
+ $mech->content_contains( '<td>test@example.com' );
+ $mech->content_contains( '<td>test note' );
+
+ $mech->submit_form_ok( { with_fields => {
+ category => 'test/category',
+ email => 'test@example.com',
+ note => 'test/note',
+ } } );
+ $mech->get_ok('/admin/council_edit/2650/test/category');
+
+};
+
+subtest 'check contact editing' => sub {
+ $mech->get_ok('/admin/council_edit/2650/test%20category');
+
+ $mech->submit_form_ok( { with_fields => {
+ email => 'test2@example.com',
+ note => 'test2 note',
+ } } );
+
+ $mech->content_contains( 'test category' );
+ $mech->content_contains( '<td>test2@example.com' );
+ $mech->content_contains( '<td>test2 note' );
+
+ $mech->get_ok('/admin/council_edit/2650/test%20category');
+ $mech->content_contains( '<td><strong>test2@example.com' );
+};
+
+subtest 'check contact updating' => sub {
+ $mech->get_ok('/admin/council_edit/2650/test%20category');
+ $mech->content_like(qr{test2\@example.com</strong>[^<]*</td>[^<]*<td>No}s);
+
+ $mech->get_ok('/admin/council_contacts/2650');
+
+ $mech->form_number( 1 );
+ $mech->tick( 'confirmed', 'test category' );
+ $mech->submit_form_ok({form_number => 1});
+
+ $mech->content_like(qr'test2@example.com</td>[^<]*<td>Yes's);
+ $mech->get_ok('/admin/council_edit/2650/test%20category');
+ $mech->content_like(qr{test2\@example.com[^<]*</td>[^<]*<td><strong>Yes}s);
+};
+
+subtest 'check text output' => sub {
+ $mech->get_ok('/admin/council_contacts/2650?text=1');
+ is $mech->content_type, 'text/plain';
+ $mech->content_contains('test category');
+};
+
+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',
+ email => $user->email,
+ anonymous => 0,
+ },
+ changes => {
+ title => 'Edited Report',
+ },
+ log_count => 1,
+ 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',
+ email => $user->email,
+ anonymous => 0,
+ },
+ changes => {
+ detail => 'Edited Detail',
+ },
+ log_count => 2,
+ log_entries => [ qw/edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'edit report user name',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'confirmed',
+ name => 'Test User',
+ email => $user->email,
+ anonymous => 0,
+ },
+ changes => {
+ name => 'Edited User',
+ },
+ log_count => 3,
+ log_entries => [ qw/edit edit edit/ ],
+ resend => 0,
+ user => $user,
+ },
+ {
+ description => 'edit report user email',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'confirmed',
+ name => 'Edited User',
+ email => $user->email,
+ anonymous => 0,
+ },
+ changes => {
+ email => $user2->email,
+ },
+ log_count => 4,
+ log_entries => [ qw/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',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'unconfirmed'
+ },
+ log_count => 5,
+ log_entries => [ qw/state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'change state to confirmed',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'unconfirmed',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'confirmed'
+ },
+ log_count => 6,
+ log_entries => [ qw/state_change state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'change state to fixed',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'confirmed',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'fixed'
+ },
+ log_count => 7,
+ log_entries => [ qw/state_change state_change state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'change state to hidden',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'fixed',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'hidden'
+ },
+ log_count => 8,
+ log_entries => [ qw/state_change state_change state_change state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'edit and change state',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'hidden',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 0,
+ },
+ changes => {
+ state => 'confirmed',
+ anonymous => 1,
+ },
+ log_count => 10,
+ log_entries => [ qw/edit state_change state_change state_change state_change state_change edit edit edit edit/ ],
+ resend => 0,
+ },
+ {
+ description => 'resend',
+ fields => {
+ title => 'Edited Report',
+ detail => 'Edited Detail',
+ state => 'confirmed',
+ name => 'Edited User',
+ email => $user2->email,
+ anonymous => 1,
+ },
+ changes => {
+ },
+ log_count => 11,
+ log_entries => [ qw/resend edit state_change state_change state_change state_change state_change edit edit edit edit/ ],
+ resend => 1,
+ },
+) {
+ subtest $test->{description} => sub {
+ $log_entries->reset;
+ $mech->get_ok("/admin/report_edit/$report_id");
+
+ 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, $test->{log_count}, 'log entry count';
+ is $log_entries->next->action, $_, 'log entry added' for @{ $test->{log_entries} };
+
+ $report->discard_changes;
+
+ if ( $report->state eq 'confirmed' ) {
+ $mech->content_contains( 'type="submit" name="resend"', 'no resend button' );
+ } else {
+ $mech->content_lacks( 'type="submit" name="resend"', 'no resend button' );
+ }
+
+ is $report->$_, $test->{changes}->{$_}, "$_ updated" for grep { $_ ne 'email' } 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';
+ }
+ };
+}
+
+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,
+ email => $report->user->email,
+ anonymous => 1,
+ };
+
+ is_deeply( $mech->visible_form_values(), $fields, 'initial form values' );
+
+ my $changes = {
+ email => 'test3@example.com'
+ };
+
+ $user3 =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test3@example.com', name => 'Test User 2' } );
+
+ 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', name => 'Test User 2' } );
+
+ $report->discard_changes;
+
+ ok $user3, 'new user created';
+ is $report->user_id, $user3->id, 'user changed to new user';
+};
+
+$log_entries->delete;
+
+my $update = FixMyStreet::App->model('DB::Comment')->create(
+ {
+ text => 'this is an update',
+ user => $user,
+ state => 'confirmed',
+ problem => $report,
+ mark_fixed => 0,
+ anonymous => 1,
+ }
+);
+
+$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,
+ email => '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,
+ email => '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,
+ email => '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,
+ email => $update->user->email,
+ email => 'test@example.com',
+ },
+ changes => {
+ email => '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,
+ email => '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,
+ email => 'test2@example.com',
+ },
+ changes => {
+ text => 'this is a twice changed update',
+ state => 'hidden',
+ },
+ 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 'email' } keys %{ $test->{changes} };
+
+ if ( $test->{user} ) {
+ is $update->user->id, $test->{user}->id, 'update user';
+ }
+ };
+}
+
+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 => 'hidden',
+ name => 'A User',
+ anonymous => 0,
+ email => '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 'hiding comment marked as fixed reopens report' => sub {
+ $update->mark_fixed( 1 );
+ $update->update;
+
+ $report->state('fixed');
+ $report->update;
+
+
+ my $fields = {
+ text => 'this is a changed update',
+ state => 'hidden',
+ name => 'A User',
+ anonymous => 0,
+ email => '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');
+};
+
+$log_entries->delete;
+
+subtest 'report search' => sub {
+ $update->state('confirmed');
+ $update->user($report->user);
+ $update->update;
+
+ $mech->get_ok('/admin/search_reports');
+ $mech->get_ok('/admin/search_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/search_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/search_reports?search=' . $report->user->email);
+ $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $u_id \s* </td>}xs );
+
+ $report->state('hidden');
+ $report->update;
+
+ $mech->get_ok('/admin/search_reports?search=' . $report->user->email);
+ $mech->content_like( qr{<tr [^>]*hidden[^>]*> \s* <td> \s* $r_id \s* </td>}xs );
+};
+
+$mech->delete_user( $user );
+$mech->delete_user( $user2 );
+$mech->delete_user( $user3 );
+$mech->delete_user( 'test4@example.com' );
+
+done_testing();
diff --git a/t/app/controller/alert.t b/t/app/controller/alert.t
new file mode 100644
index 000000000..b8e38ec92
--- /dev/null
+++ b/t/app/controller/alert.t
@@ -0,0 +1,66 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'FixMyStreet::App';
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+
+ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' );
+
+# check that we can get the page
+$mech->get_ok('/alert');
+$mech->title_like(qr/^Local RSS feeds and email alerts/);
+$mech->content_contains('Local RSS feeds and email alerts');
+$mech->content_contains('html lang="en-gb"');
+
+# check that we can get list page
+$mech->get_ok('/alert/list');
+$mech->title_like(qr/^Local RSS feeds and email alerts/);
+$mech->content_contains('Local RSS feeds and email alerts');
+$mech->content_contains('html lang="en-gb"');
+
+$mech->get_ok('/alert/list?pc=EH99 1SP');
+$mech->title_like(qr/^Local RSS feeds and email alerts/);
+$mech->content_contains('Here are the types of local problem alerts for &lsquo;EH99&nbsp;1SP&rsquo;');
+$mech->content_contains('html lang="en-gb"');
+$mech->content_contains('Problems within 8.5km');
+$mech->content_contains('rss/pc/EH991SP/2');
+$mech->content_contains('rss/pc/EH991SP/5');
+$mech->content_contains('rss/pc/EH991SP/10');
+$mech->content_contains('rss/pc/EH991SP/20');
+$mech->content_contains('Problems within City of Edinburgh');
+$mech->content_contains('Problems within City Centre ward');
+$mech->content_contains('/rss/reports/City+of+Edinburgh');
+$mech->content_contains('/rss/reports/City+of+Edinburgh/City+Centre');
+$mech->content_contains('council:2651:City_of_Edinburgh');
+$mech->content_contains('ward:2651:20728:City_of_Edinburgh:City_Centre');
+
+$mech->get_ok('/alert/list?pc=High Street');
+$mech->content_contains('We found more than one match for that location');
+
+$mech->get_ok('/alert/list?pc=');
+$mech->content_contains('hat location does not appear to be covered by a council');
+
+$mech->get_ok('/alert/list?pc=GL502PR');
+$mech->content_contains('Problems within the boundary of');
+
+$mech->get_ok('/alert/subscribe?rss=1&type=local&pc=ky16+8yg&rss=Give+me+an+RSS+feed&rznvy=' );
+$mech->content_contains('Please select the feed you want');
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=invalid:1000:A_Locationtype=local&pc=ky16+8yg&rss=Give+me+an+RSS+feed&rznvy=');
+$mech->content_contains('Illegal feed selection');
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=area:1000:Birmingham');
+is $mech->uri->path, '/rss/reports/Birmingham';
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=area:1000:1001:Cheltenham:Lansdown');
+is $mech->uri->path, '/rss/area/Cheltenham/Lansdown';
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=council:1000:Gloucestershire');
+is $mech->uri->path, '/rss/reports/Gloucestershire';
+
+$mech->get_ok('/alert/subscribe?rss=1&feed=ward:1000:1001:Cheltenham:Lansdown');
+is $mech->uri->path, '/rss/reports/Cheltenham/Lansdown';
+
+done_testing();
diff --git a/t/app/controller/alert_new.t b/t/app/controller/alert_new.t
new file mode 100644
index 000000000..580a5ad9a
--- /dev/null
+++ b/t/app/controller/alert_new.t
@@ -0,0 +1,470 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+foreach my $test (
+ {
+ email => 'test@example.com',
+ type => 'area_problems',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test@example.com&feed=area:1000:A_Location',
+ param1 => 1000
+ },
+ {
+ email => 'test@example.com',
+ type => 'council_problems',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test@example.com&feed=council:1000:A_Location',
+ param1 => 1000,
+ param2 => 1000,
+ },
+ {
+ email => 'test@example.com',
+ type => 'ward_problems',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test@example.com&feed=ward:1000:1001:A_Location:Diff_Location',
+ param1 => 1000,
+ param2 => 1001,
+ },
+ {
+ email => 'test@example.com',
+ type => 'local_problems',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test@example.com&feed=local:10.2:20.1',
+ param1 => 20.1,
+ param2 => 10.2,
+ },
+ {
+ email => 'test@example.com',
+ type => 'new_updates',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri => '/alert/subscribe?type=updates&rznvy=test@example.com&id=1',
+ param1 => 1,
+ }
+ )
+{
+ subtest "$test->{type} alert correctly created" => sub {
+ $mech->clear_emails_ok;
+
+ my $type = $test->{type};
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => $test->{email} } );
+
+ # we don't want an alert
+ if ($user) {
+ $mech->delete_user($user);
+ }
+
+ $mech->get_ok( $test->{uri} );
+ $mech->content_contains( $test->{content} );
+
+ $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => $test->{email} } );
+
+ ok $user, 'user created for alert';
+
+ my $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type,
+ parameter => $test->{param1},
+ parameter2 => $test->{param2},
+ confirmed => 0,
+ }
+ );
+
+ ok $alert, "Found the alert";
+
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/$test->{email_text}/i, "Correct email text";
+
+ my ( $url, $url_token ) = $email->body =~ m{(http://\S+/A/)(\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ my $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'alert'
+ }
+ );
+ ok $token, 'Token found in database';
+ ok $alert->id == $token->data->{id}, 'token alertid matches alert id';
+
+ $mech->clear_emails_ok;
+
+ my $existing_id = $alert->id;
+ my $existing_token = $url_token;
+
+ $mech->get_ok( $test->{uri} );
+
+ $email = $mech->get_email;
+ ok $email, 'got a second email';
+
+ ($url_token) = $email->body =~ m{http://\S+/A/(\S+)};
+ ok $url_token ne $existing_token, 'sent out a new token';
+
+ $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'alert'
+ }
+ );
+
+ ok $token, 'new token found in database';
+ ok $token->data->{id} == $existing_id, 'subscribed to existing alert';
+
+ $mech->get_ok("/A/$url_token");
+ $mech->content_contains('successfully confirmed');
+
+ $alert =
+ FixMyStreet::App->model('DB::Alert')->find( { id => $existing_id, } );
+
+ ok $alert->confirmed, 'alert set to confirmed';
+ $mech->delete_user($user);
+ };
+}
+
+foreach my $test (
+ {
+ email => 'test-new@example.com',
+ type => 'area',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri =>
+'/alert/subscribe?type=local&rznvy=test-new@example.com&feed=area:1000:A_Location',
+ param1 => 1000
+ }
+ )
+{
+ subtest "use existing unlogged in user in a alert" => sub {
+ $mech->log_out_ok();
+
+ my $type = $test->{type} . '_problems';
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => $test->{email} } );
+
+ my $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type
+ }
+ );
+ # clear existing data so we can be sure we're creating it
+ ok $alert->delete() if $alert;
+
+ $mech->get_ok( $test->{uri} );
+
+ $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type,
+ parameter => $test->{param1},
+ parameter2 => $test->{param2},
+ confirmed => 0,
+ }
+ );
+
+ $mech->content_contains( 'Now check your email' );
+
+ ok $alert, 'New alert created with existing user';
+ $mech->delete_user($user);
+ };
+}
+
+foreach my $test (
+ {
+ desc => 'logged in user signing up',
+ email => 'test-sign-in@example.com',
+ type => 'council',
+ param1 => 2651,
+ param2 => 2651,
+ confirmed => 1,
+ }
+ )
+{
+ subtest $test->{desc} => sub {
+ my $type = $test->{type} . '_problems';
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => $test->{email} } );
+
+ $mech->log_in_ok( $test->{email} );
+ $mech->clear_emails_ok;
+
+ $mech->get_ok('/alert/list?pc=EH991SP');
+ $mech->set_visible( [ radio => 'council:2651:City_of_Edinburgh' ] );
+ $mech->click('alert');
+
+ my $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type,
+ parameter => $test->{param1},
+ parameter2 => $test->{param2},
+ confirmed => $test->{confirmed},
+ }
+ );
+
+ ok $alert, 'New alert created with logged in user';
+ $mech->email_count_is( 0 );
+ $mech->delete_user($user);
+ };
+}
+
+for my $test (
+ {
+ email => 'test@example.com',
+ type => 'new_updates',
+ content => 'your alert will not be activated',
+ email_text => 'confirm the alert',
+ uri => '/alert/subscribe?type=updates&rznvy=test@example.com&id=1',
+ param1 => 1,
+ }
+ )
+{
+ subtest "cannot sign up for alert if in abuse table" => sub {
+ $mech->clear_emails_ok;
+
+ my $type = $test->{type};
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => $test->{email} } );
+
+ # we don't want an alert
+ my $alert;
+ if ($user) {
+ $mech->delete_user($user);
+ }
+
+ my $abuse =
+ FixMyStreet::App->model('DB::Abuse')
+ ->find_or_create( { email => $test->{email} } );
+
+ $mech->get_ok( $test->{uri} );
+ $mech->content_contains( $test->{content} );
+
+ $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => $test->{email} } );
+
+ ok $user, 'user created for alert';
+
+ $alert = FixMyStreet::App->model('DB::Alert')->find(
+ {
+ user => $user,
+ alert_type => $type,
+ parameter => $test->{param1},
+ parameter2 => $test->{param2},
+ confirmed => 0,
+ }
+ );
+
+ ok $alert, "Found the alert";
+
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/$test->{email_text}/i, "Correct email text";
+
+ my ( $url, $url_token ) = $email->body =~ m{(http://\S+/A/)(\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ my $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'alert'
+ }
+ );
+ ok $token, 'Token found in database';
+ ok $alert->id == $token->data->{id}, 'token alertid matches alert id';
+
+ $mech->clear_emails_ok;
+
+ $mech->get_ok("/A/$url_token");
+ $mech->content_contains('error confirming');
+
+ $alert->discard_changes;
+
+ ok !$alert->confirmed, 'alert not set to confirmed';
+
+ $abuse->delete;
+ $mech->delete_user($user);
+ };
+}
+
+subtest "Test two-tier council alerts" => sub {
+ for my $alert (
+ { feed => "local:51.896269:-2.093063", result => '/rss/l/51.896269,-2.093063' },
+ { feed => "area:2326:Cheltenham", result => '/rss/area/Cheltenham' },
+ { feed => "area:2326:4544:Cheltenham:Lansdown", result => '/rss/area/Cheltenham/Lansdown' },
+ { feed => "area:2226:Gloucestershire", result => '/rss/area/Gloucestershire' },
+ { feed => "area:2226:14949:Gloucestershire:Lansdown%2C_Park_and_Warden_Hill",
+ result => '/rss/area/Gloucestershire/Lansdown%2C+Park+and+Warden+Hill'
+ },
+ { feed => "council:2326:Cheltenham", result => '/rss/reports/Cheltenham' },
+ { feed => "ward:2326:4544:Cheltenham:Lansdown", result => '/rss/reports/Cheltenham/Lansdown' },
+ { feed => "council:2226:Gloucestershire", result => '/rss/reports/Gloucestershire' },
+ { feed => "ward:2226:14949:Gloucestershire:Lansdown%2C_Park_and_Warden_Hill",
+ result => '/rss/reports/Gloucestershire/Lansdown%2C+Park+and+Warden+Hill'
+ },
+ ) {
+ $mech->get_ok( '/alert/list?pc=GL502PR' );
+ $mech->submit_form_ok( {
+ button => 'rss',
+ with_fields => {
+ feed => $alert->{feed},
+ }
+ } );
+ is $mech->uri->path, $alert->{result};
+ }
+};
+
+subtest "Test normal alert signups and that alerts are sent" => sub {
+ my $user1 = FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'reporter@example.com', name => 'Reporter User' } );
+ ok $user1, "created test user";
+ $user1->alerts->delete;
+
+ my $user2 = FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'alerts@example.com', name => 'Alert User' } );
+ ok $user2, "created test user";
+ $user2->alerts->delete;
+
+ for my $alert (
+ {
+ fields => {
+ feed => 'local:55.951963:-3.189944',
+ rznvy => $user2->email,
+ },
+ email_confirm => 1
+ },
+ {
+ fields => {
+ feed => 'council:2651:City_of_Edinburgh',
+ }
+ },
+ ) {
+ $mech->get_ok( '/alert' );
+ $mech->submit_form_ok( { with_fields => { pc => 'EH11BB' } } );
+ $mech->submit_form_ok( {
+ button => 'alert',
+ with_fields => $alert->{fields},
+ } );
+ if ( $alert->{email_confirm} ) {
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+ my ( $url, $url_token ) = $email->body =~ m{http://\S+(/A/(\S+))};
+ my $token = FixMyStreet::App->model('DB::Token')->find( { token => $url_token, scope => 'alert' } );
+ $mech->get_ok( $url );
+ $mech->content_contains('successfully confirmed');
+ } else {
+ $mech->content_contains('successfully created');
+ }
+ }
+
+ my $dt = DateTime->now()->add( days => 2);
+
+ my $report_time = '2011-03-01 12:00:00';
+ my $report = FixMyStreet::App->model('DB::Problem')->find_or_create( {
+ postcode => 'EH1 1BB',
+ council => '2651',
+ areas => ',11808,135007,14419,134935,2651,20728,',
+ category => 'Street lighting',
+ title => 'Testing',
+ detail => 'Testing Detail',
+ used_map => 1,
+ name => $user1->name,
+ anonymous => 0,
+ state => 'confirmed',
+ confirmed => $dt,
+ lastupdate => $dt,
+ whensent => $dt->clone->add( minutes => 5 ),
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 1,
+ latitude => '55.951963',
+ longitude => '-3.189944',
+ user_id => $user1->id,
+ } );
+ my $report_id = $report->id;
+ ok $report, "created test report - $report_id";
+
+ my $alert = FixMyStreet::App->model('DB::Alert')->create( {
+ parameter => $report_id,
+ alert_type => 'new_updates',
+ user => $user1,
+ } )->confirm;
+ ok $alert, 'created alert for reporter';
+
+ my $update = FixMyStreet::App->model('DB::Comment')->create( {
+ problem_id => $report_id,
+ user_id => $user2->id,
+ name => 'Other User',
+ mark_fixed => 'false',
+ text => 'This is some update text',
+ state => 'confirmed',
+ confirmed => $dt->clone->add( hours => 7 ),
+ anonymous => 'f',
+ } );
+ my $update_id = $update->id;
+ ok $update, "created test update - $update_id";
+
+ $update = FixMyStreet::App->model('DB::Comment')->create( {
+ problem_id => $report_id,
+ user_id => $user2->id,
+ name => 'Anonymous User',
+ mark_fixed => 'false',
+ text => 'This is some more update text',
+ state => 'confirmed',
+ confirmed => $dt->clone->add( hours => 8 ),
+ anonymous => 't',
+ } );
+ $update_id = $update->id;
+ ok $update, "created test update - $update_id";
+
+ FixMyStreet::App->model('DB::AlertType')->email_alerts();
+ $mech->email_count_is(3);
+ my @emails = $mech->get_email;
+ my $count;
+ for (@emails) {
+ $count++ if $_->body =~ /The following updates have been left on this problem:/;
+ $count++ if $_->body =~ /The following new problems have been reported to City of\s*Edinburgh Council:/;
+ $count++ if $_->body =~ /The following nearby problems have been added:/;
+ }
+ is $count, 3, 'Three emails with the right things in them';
+
+ my $email = $emails[0];
+ like $email->body, qr/Other User/, 'Update name given';
+ unlike $email->body, qr/Anonymous User/, 'Update name not given';
+
+ my ( $url, $url_token ) = $emails[0]->body =~ m{http://\S+(/A/(\S+))};
+ $mech->get_ok( $url );
+ $mech->content_contains('successfully deleted');
+
+ $mech->delete_user($user1);
+ $mech->delete_user($user2);
+};
+
+done_testing();
diff --git a/t/app/controller/around.t b/t/app/controller/around.t
new file mode 100644
index 000000000..ca1694b49
--- /dev/null
+++ b/t/app/controller/around.t
@@ -0,0 +1,81 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+subtest "check that if no query we get sent back to the homepage" => sub {
+ $mech->get_ok('/around');
+ is $mech->uri->path, '/around', "still at '/around'";
+};
+
+# x,y requests were generated by the old map code. We keep the behavior for
+# historic links
+subtest "redirect x,y requests to lat/lon (301 - permanent)" => sub {
+
+ $mech->get_ok('/around?x=3281&y=1113');
+
+ # did we redirect to lat,lon?
+ is $mech->uri->path, '/around', "still on /around";
+ is_deeply { $mech->uri->query_form },
+ { lat => 51.499825, lon => -0.140137, zoom => 3 },
+ "lat,lon correctly set";
+
+ # was it a 301?
+ is $mech->res->code, 200, "got 200 for final destination";
+ is $mech->res->previous->code, 301, "got 301 for redirect";
+
+};
+
+# test various locations on inital search box
+foreach my $test (
+ {
+ pc => '', #
+ errors => [],
+ pc_alternatives => [],
+ },
+ {
+ pc => 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ errors => ['Sorry, we could not find that location.'],
+ pc_alternatives => [],
+ },
+ {
+ pc => 'Glenthorpe Ct, Katy, TX 77494, USA',
+ errors =>
+ ['Sorry, we could not find that location.'],
+ pc_alternatives => [],
+ },
+ )
+{
+ subtest "test bad pc value '$test->{pc}'" => sub {
+ $mech->get_ok('/');
+ $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } },
+ "bad location" );
+ is_deeply $mech->page_errors, $test->{errors},
+ "expected errors for pc '$test->{pc}'";
+ is_deeply $mech->pc_alternatives, $test->{pc_alternatives},
+ "expected alternatives for pc '$test->{pc}'";
+ };
+}
+
+# check that exact queries result in the correct lat,lng
+foreach my $test (
+ {
+ pc => 'SW1A 1AA',
+ latitude => '51.50101',
+ longitude => '-0.141587',
+ },
+ )
+{
+ subtest "check lat/lng for '$test->{pc}'" => sub {
+ $mech->get_ok('/');
+ $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } },
+ "good location" );
+ is_deeply $mech->form_errors, [], "no errors for pc '$test->{pc}'";
+ is_deeply $mech->extract_location, $test,
+ "got expected location for pc '$test->{pc}'";
+ };
+}
+
+done_testing();
diff --git a/t/app/controller/auth.t b/t/app/controller/auth.t
new file mode 100644
index 000000000..fef45ac90
--- /dev/null
+++ b/t/app/controller/auth.t
@@ -0,0 +1,227 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+my $test_email = 'test@example.com';
+my $test_password = 'foobar';
+$mech->delete_user($test_email);
+
+END {
+ $mech->delete_user($test_email);
+ done_testing();
+}
+
+$mech->get_ok('/auth');
+
+# check that we can't reach a page that is only available to authenticated users
+$mech->not_logged_in_ok;
+
+# check that submitting form with no / bad email creates an error.
+$mech->get_ok('/auth');
+
+for my $test (
+ [ '' => 'enter your email' ],
+ [ 'not an email' => 'check your email address is correct' ],
+ [ 'bob@foo' => 'check your email address is correct' ],
+ [ 'bob@foonaoedudnueu.co.uk' => 'check your email address is correct' ],
+ )
+{
+ my ( $email, $error_message ) = @$test;
+ pass "--- testing bad email '$email' gives error '$error_message'";
+ $mech->get_ok('/auth');
+ $mech->content_lacks($error_message);
+ $mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => { email => $email, },
+ button => 'email_sign_in',
+ },
+ "try to create an account with email '$email'"
+ );
+ is $mech->uri->path, '/auth', "still on auth page";
+ $mech->content_contains($error_message);
+}
+
+# create a new account
+$mech->clear_emails_ok;
+$mech->get_ok('/auth');
+$mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => { email => $test_email, },
+ button => 'email_sign_in',
+ },
+ "create an account for '$test_email'"
+);
+
+# check that we are not logged in yet
+$mech->not_logged_in_ok;
+
+# check that we got one email
+{
+ $mech->email_count_is(1);
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+ is $email->header('Subject'), "Your FixMyStreet.com account details",
+ "subject is correct";
+ is $email->header('To'), $test_email, "to is correct";
+
+ # extract the link
+ my ($link) = $email->body =~ m{(http://\S+)};
+ ok $link, "Found a link in email '$link'";
+
+ # check that the user does not exist
+ sub get_user {
+ FixMyStreet::App->model('DB::User')->find( { email => $test_email } );
+ }
+ ok !get_user(), "no user exists";
+
+ # visit the confirm link (with bad token) and check user no confirmed
+ $mech->get_ok( $link . 'XXX' );
+ ok !get_user(), "no user exists";
+ $mech->not_logged_in_ok;
+
+ # visit the confirm link and check user is confirmed
+ $mech->get_ok($link);
+ ok get_user(), "user created";
+ is $mech->uri->path, '/my', "redirected to the 'my' section of site";
+ $mech->logged_in_ok;
+
+ # logout and try to use the token again
+ $mech->log_out_ok;
+ $mech->get_ok($link);
+ is $mech->uri, $link, "not logged in";
+ $mech->content_contains( 'Link too old or already used',
+ 'token now invalid' );
+ $mech->not_logged_in_ok;
+}
+
+# get a sign in email and change password
+{
+ $mech->clear_emails_ok;
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => {
+ email => "$test_email",
+ r => 'faq', # Just as a test
+ },
+ button => 'email_sign_in',
+ },
+ "email_sign_in with '$test_email'"
+ );
+
+ # rest is as before so no need to test
+
+ # follow link and change password - check not prompted for old password
+ $mech->not_logged_in_ok;
+
+ $mech->email_count_is(1);
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+ my ($link) = $email->body =~ m{(http://\S+)};
+ $mech->get_ok($link);
+ is $mech->uri->path, '/faq', "redirected to the Help page";
+
+ $mech->get_ok('/auth/change_password');
+
+ ok my $form = $mech->form_name('change_password'),
+ "found change password form";
+ is_deeply [ sort grep { $_ } map { $_->name } $form->inputs ], #
+ [ 'confirm', 'new_password' ],
+ "check we got expected fields (ie not old_password)";
+
+ # check the various ways the form can be wrong
+ for my $test (
+ { new => '', conf => '', err => 'enter a password', },
+ { new => 'secret', conf => '', err => 'do not match', },
+ { new => '', conf => 'secret', err => 'do not match', },
+ { new => 'secret', conf => 'not_secret', err => 'do not match', },
+ )
+ {
+ $mech->get_ok('/auth/change_password');
+ $mech->content_lacks( $test->{err}, "did not find expected error" );
+ $mech->submit_form_ok(
+ {
+ form_name => 'change_password',
+ fields =>
+ { new_password => $test->{new}, confirm => $test->{conf}, },
+ },
+ "change_password with '$test->{new}' and '$test->{conf}'"
+ );
+ $mech->content_contains( $test->{err}, "found expected error" );
+ }
+
+ my $user =
+ FixMyStreet::App->model('DB::User')->find( { email => $test_email } );
+ ok $user, "got a user";
+ ok !$user->password, "user has no password";
+
+ $mech->get_ok('/auth/change_password');
+ $mech->submit_form_ok(
+ {
+ form_name => 'change_password',
+ fields =>
+ { new_password => $test_password, confirm => $test_password, },
+ },
+ "change_password with '$test_password' and '$test_password'"
+ );
+ is $mech->uri->path, '/auth/change_password',
+ "still on change password page";
+ $mech->content_contains( 'password has been changed',
+ "found password changed" );
+
+ $user->discard_changes();
+ ok $user->password, "user now has a password";
+}
+
+foreach my $remember_me ( '1', '0' ) {
+ subtest "sign in using valid details (remember_me => '$remember_me')" => sub {
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => {
+ email => $test_email,
+ password_sign_in => $test_password,
+ remember_me => ( $remember_me ? 1 : undef ),
+ },
+ button => 'sign_in',
+ },
+ "sign in with '$test_email' & '$test_password"
+ );
+ is $mech->uri->path, '/my', "redirected to correct page";
+
+ my $expiry = $mech->session_cookie_expiry;
+ $remember_me
+ ? cmp_ok( $expiry, '>', 86400, "long expiry time" )
+ : is( $expiry, 0, "no expiry time" );
+
+ # logout
+ $mech->log_out_ok;
+ };
+}
+
+# try to sign in with bad details
+$mech->get_ok('/auth');
+$mech->submit_form_ok(
+ {
+ form_name => 'general_auth',
+ fields => {
+ email => $test_email,
+ password_sign_in => 'not the password',
+ },
+ button => 'sign_in',
+ },
+ "sign in with '$test_email' & '$test_password"
+);
+is $mech->uri->path, '/auth', "redirected to correct page";
+$mech->content_contains( 'problem with your email/password combination', 'found error message' );
+
+# more test:
+# TODO: test that email are always lowercased
diff --git a/t/app/controller/contact.t b/t/app/controller/contact.t
new file mode 100644
index 000000000..bbb3a0f83
--- /dev/null
+++ b/t/app/controller/contact.t
@@ -0,0 +1,275 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+$mech->get_ok('/contact');
+$mech->title_like(qr/Contact Us/);
+$mech->content_contains("We'd love to hear what you think about this site");
+
+my $problem_main;
+
+for my $test (
+ {
+ name => 'A User',
+ email => 'problem_report_test@example.com',
+ title => 'Some problem or other',
+ detail => 'More detail on the problem',
+ postcode => 'EH99 1SP',
+ confirmed => '2011-05-04 10:44:28.145168',
+ anonymous => 0,
+ meta => 'Reported by A User at 10:44, Wednesday 4 May 2011',
+ main => 1,
+ },
+ {
+ name => 'A User',
+ email => 'problem_report_test@example.com',
+ title => 'A different problem',
+ detail => 'More detail on the different problem',
+ postcode => 'EH99 1SP',
+ confirmed => '2011-05-03 13:24:28.145168',
+ anonymous => 1,
+ meta => 'Reported anonymously at 13:24, Tuesday 3 May 2011',
+ },
+ {
+ name => 'A User',
+ email => 'problem_report_test@example.com',
+ title => 'A different problem',
+ detail => 'More detail on the different problem',
+ postcode => 'EH99 1SP',
+ confirmed => '2011-05-03 13:24:28.145168',
+ anonymous => 1,
+ meta => 'Reported anonymously at 13:24, Tuesday 3 May 2011',
+ update => {
+ name => 'Different User',
+ email => 'commenter@example.com',
+ text => 'This is an update',
+ },
+ },
+ )
+{
+ subtest 'check reporting a problem displays correctly' => sub {
+ my $user = FixMyStreet::App->model('DB::User')->find_or_create(
+ {
+ name => $test->{name},
+ email => $test->{email}
+ }
+ );
+
+ my $problem = FixMyStreet::App->model('DB::Problem')->create(
+ {
+ title => $test->{title},
+ detail => $test->{detail},
+ postcode => $test->{postcode},
+ confirmed => $test->{confirmed},
+ name => $test->{name},
+ anonymous => $test->{anonymous},
+ state => 'confirmed',
+ user => $user,
+ latitude => 0,
+ longitude => 0,
+ areas => 0,
+ used_map => 0,
+ }
+ );
+
+ my $update;
+
+ if ( $test->{update} ) {
+ my $update_info = $test->{update};
+ my $update_user = FixMyStreet::App->model('DB::User')->find_or_create(
+ {
+ name => $update_info->{name},
+ email => $update_info->{email}
+ }
+ );
+
+ $update = FixMyStreet::App->model('DB::Comment')->create(
+ {
+ problem_id => $problem->id,
+ user => $update_user,
+ state => 'confirmed',
+ text => $update_info->{text},
+ confirmed => \'ms_current_timestamp()',
+ mark_fixed => 'f',
+ anonymous => 'f',
+ }
+ );
+ }
+
+ ok $problem, 'succesfully create a problem';
+
+ if ( $update ) {
+ $mech->get_ok( '/contact?id=' . $problem->id . '&update_id=' . $update->id );
+ $mech->content_contains('reporting the following update');
+ $mech->content_contains( $test->{update}->{text} );
+ } else {
+ $mech->get_ok( '/contact?id=' . $problem->id );
+ $mech->content_contains('reporting the following problem');
+ $mech->content_contains( $test->{title} );
+ $mech->content_contains( $test->{meta} );
+ }
+
+ $update->delete if $update;
+ if ($test->{main}) {
+ $problem_main = $problem;
+ } else {
+ $problem->delete;
+ }
+ };
+}
+
+for my $test (
+ {
+ fields => {
+ em => ' ',
+ name => '',
+ subject => '',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [
+ 'Please enter your name',
+ 'Please enter your email',
+ 'Please enter a subject',
+ 'Please write a message',
+ ]
+ },
+ {
+ fields => {
+ em => 'invalidemail',
+ name => '',
+ subject => '',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [
+ 'Please enter your name',
+ 'Please enter a valid email address',
+ 'Please enter a subject',
+ 'Please write a message',
+ ]
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => '',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [ 'Please enter a subject', 'Please write a message', ]
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [ 'Please write a message', ]
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => ' ',
+ message => '',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [ 'Please enter a subject', 'Please write a message', ]
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => ' ',
+ },
+ page_errors =>
+ [ 'There were problems with your report. Please see below.', ],
+ field_errors => [ 'Please write a message', ]
+ },
+ {
+ url => '/contact?id=' . $problem_main->id,
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => 'A message',
+ id => 'invalid',
+ },
+ page_errors => [ 'Illegal ID' ],
+ field_errors => []
+ },
+ )
+{
+ subtest 'check submit page error handling' => sub {
+ $mech->get_ok( $test->{url} ? $test->{url} : '/contact' );
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+ is_deeply $mech->page_errors, $test->{page_errors}, 'page errors';
+ is_deeply $mech->form_errors, $test->{field_errors}, 'field_errors';
+
+ # we santise this when we submit so need to remove it
+ delete $test->{fields}->{id}
+ if $test->{fields}->{id} and $test->{fields}->{id} eq 'invalid';
+ is_deeply $mech->visible_form_values, $test->{fields}, 'form values';
+ };
+}
+
+for my $test (
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => 'A message',
+ },
+ },
+ {
+ fields => {
+ em => 'test@example.com',
+ name => 'A name',
+ subject => 'A subject',
+ message => 'A message',
+ id => $problem_main->id,
+ },
+ },
+
+ )
+{
+ subtest 'check email sent correctly' => sub {
+ $mech->clear_emails_ok;
+ if ($test->{fields}{id}) {
+ $mech->get_ok('/contact?id=' . $test->{fields}{id});
+ } else {
+ $mech->get_ok('/contact');
+ }
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+ $mech->content_contains('Thanks for your feedback');
+ $mech->email_count_is(1);
+
+ my $email = $mech->get_email;
+
+ is $email->header('Subject'), 'FMS message: ' . $test->{fields}->{subject}, 'subject';
+ is $email->header('From'), "\"$test->{fields}->{name}\" <$test->{fields}->{em}>", 'from';
+ like $email->body, qr/$test->{fields}->{message}/, 'body';
+ like $email->body, qr/Sent by contact.cgi on \S+. IP address (?:\d{1,3}\.){3,}\d{1,3}/, 'body footer';
+ my $problem_id = $test->{fields}{id};
+ like $email->body, qr/Complaint about report $problem_id/, 'reporting a report'
+ if $test->{fields}{id};
+ };
+}
+
+$problem_main->delete;
+
+done_testing();
diff --git a/t/app/controller/council.t b/t/app/controller/council.t
new file mode 100644
index 000000000..11898995a
--- /dev/null
+++ b/t/app/controller/council.t
@@ -0,0 +1,14 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'FixMyStreet::App';
+use_ok( 'FixMyStreet::App::Controller::Council' );
+
+TODO: {
+ local $TODO = 'need to write some tests for this';
+
+}
+
+done_testing();
diff --git a/t/app/controller/index.t b/t/app/controller/index.t
new file mode 100644
index 000000000..bf9124ee0
--- /dev/null
+++ b/t/app/controller/index.t
@@ -0,0 +1,58 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+# check that the homepage loads
+$mech->get_ok('/');
+
+subtest "check that the form goes to /around" => sub {
+ $mech->get_ok('/');
+ is $mech->uri->path, '/', "still on '/'";
+
+ # submit form
+ $mech->submit_form_ok( { with_fields => { pc => 'SW1A 1AA', } } );
+
+ # check that we are at /around
+ is $mech->uri->path, '/around', "Got to /around";
+ is_deeply { $mech->uri->query_form }, { pc => 'SW1A 1AA' },
+ "query passed along";
+};
+
+subtest "does pc, (x,y), (e,n) or (lat,lon) go to /around" => sub {
+
+ foreach my $test ( #
+ {
+ in => { pc => 'SW1A 1AA' },
+ out => { pc => 'SW1A 1AA' },
+ },
+ {
+ in => { lat => 51.50100, lon => -0.14158 },
+ out => { lat => 51.50100, lon => -0.14158, zoom => 3 },
+ },
+ {
+ in => { x => 3281, y => 1113, },
+ out => { lat => 51.499825, lon => -0.140137, zoom => 3 },
+ },
+ {
+ in => { e => 1234, n => 4567 },
+ out => { lat => 49.808509, lon => -7.544784, zoom => 3 },
+ },
+ )
+ {
+
+ my $uri = URI->new('http://localhost/');
+ $uri->query_form( $test->{in} );
+
+ # get the uri and check for 302
+ $mech->get_ok($uri);
+
+ # check that we are at /around
+ is $mech->uri->path, '/around', "Got to /around";
+ is_deeply { $mech->uri->query_form }, $test->{out}, "query correct";
+ }
+};
+
+done_testing();
diff --git a/t/app/controller/json.t b/t/app/controller/json.t
new file mode 100644
index 000000000..468fa5b31
--- /dev/null
+++ b/t/app/controller/json.t
@@ -0,0 +1,119 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+subtest "check that a bad request produces the appropriate response" => sub {
+
+ my $bad_date = "Invalid dates supplied";
+ my $mad_date = "Start date after end date";
+ my $bad_type = "Invalid type supplied";
+
+ my %tests = (
+ '?' => $bad_date,
+ '?foo=bar' => $bad_date,
+ '?start_date=&end_date=' => $bad_date,
+ '?start_date=bad&end_date=2000-02-01' => $bad_date,
+ '?start_date=2000-01-01&end_date=bad' => $bad_date,
+ '?start_date=2000-02-31&end_date=2000-02-01' => $bad_date,
+ '?start_date=2000-01-01&end_date=2000-02-31' => $bad_date,
+
+ '?start_date=2000-02-01&end_date=2000-01-01' => $mad_date,
+
+ '?start_date=2000-01-01&end_date=2000-02-01' => $bad_type,
+ '/foo?type=foo&start_date=2000-01-01&end_date=2000-02-01' => $bad_type,
+ );
+
+ foreach my $q ( sort keys %tests ) {
+ is_deeply #
+ $mech->get_ok_json("/json/problems$q"), #
+ { error => $tests{$q} }, #
+ "correct error for query '$q'";
+ }
+
+};
+
+is_deeply #
+ $mech->get_ok_json(
+ "/json/problems/new?start_date=2000-01-01&end_date=2000-02-01"), #
+ [], #
+ "correct response";
+
+# put an entry in the database for this test
+my $user = $mech->create_user_ok('test@example.com');
+
+my $problem_args = {
+ postcode => 'sw1a 1aa',
+ council => '2501',
+ areas => ',105164,11806,11827,2247,2501,34817,42011,66045,70786,8519,',
+ category => 'test category',
+ title => 'Test title',
+ detail => 'Test detail',
+ used_map => 't',
+ name => 'Test Name',
+ created => '2000-01-01 12:00:00',
+ confirmed => '2000-01-01 12:01:00',
+ state => 'confirmed',
+ lang => 'en-gb',
+ service => '',
+ cobrand => '',
+ cobrand_data => '',
+ lastupdate => '2000-01-01 12:00:00',
+ whensent => undef,
+ send_questionnaire => 't',
+ latitude => '51.4531988729771',
+ longitude => '-0.23021896608596',
+};
+my $problem = $user->add_to_problems( { %$problem_args, anonymous => 0 } );
+my $anon_problem = $user->add_to_problems( { %$problem_args, anonymous => 1, confirmed => '2000-01-01 12:02:00' } );
+
+ok $problem, "created normal test problem";
+ok $anon_problem, "created anon test problem";
+
+is_deeply #
+ $mech->get_ok_json(
+ "/json/problems/new?start_date=2000-01-01&end_date=2000-02-01"), #
+ [
+ {
+ 'state' => 'confirmed',
+ 'longitude' => -0.23021896608596,
+ 'latitude' => 51.4531988729771,
+ 'used_map' => 1,
+ 'anonymous' => 0,
+ 'category' => 'test category',
+ 'confirmed' => '2000-01-01 12:01:00',
+ 'lastupdate' => '2000-01-01 12:00:00',
+ 'council' => 'Wandsworth Borough Council',
+ 'detail' => 'Test detail',
+ 'id' => $problem->id,
+ 'name' => 'Test Name',
+ 'service' => 'Web interface',
+ 'title' => 'Test title',
+ 'whensent' => undef
+ },
+ {
+ 'state' => 'confirmed',
+ 'longitude' => -0.23021896608596,
+ 'latitude' => 51.4531988729771,
+ 'used_map' => 1,
+ 'anonymous' => 1,
+ 'category' => 'test category',
+ 'confirmed' => '2000-01-01 12:02:00',
+ 'lastupdate' => '2000-01-01 12:00:00',
+ 'council' => 'Wandsworth Borough Council',
+ 'detail' => 'Test detail',
+ 'id' => $anon_problem->id,
+ 'name' => '',
+ 'service' => 'Web interface',
+ 'title' => 'Test title',
+ 'whensent' => undef
+ }
+ ],
+ "correct response";
+
+$mech->delete_user($user);
+
+done_testing();
diff --git a/t/app/controller/my.t b/t/app/controller/my.t
new file mode 100644
index 000000000..da509e8ed
--- /dev/null
+++ b/t/app/controller/my.t
@@ -0,0 +1,19 @@
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+
+use FixMyStreet::TestMech;
+my $mech = FixMyStreet::TestMech->new;
+
+$mech->get_ok('/my');
+is $mech->uri->path, '/auth', "got sent to the sign in page";
+
+# sign in
+my $user = $mech->log_in_ok( 'test@example.com' );
+$mech->get_ok('/my');
+is $mech->uri->path, '/my', "stayed on '/my/' page";
+
+# cleanup
+$mech->delete_user( $user );
+
diff --git a/t/app/controller/page_not_found.t b/t/app/controller/page_not_found.t
new file mode 100644
index 000000000..05e983109
--- /dev/null
+++ b/t/app/controller/page_not_found.t
@@ -0,0 +1,20 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 4;
+
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+
+my $mech = Test::WWW::Mechanize::Catalyst->new;
+
+# homepage ok
+$mech->get_ok('/');
+
+# get 404 page
+my $path_to_404 = '/bad/path/page_error_404_not_found';
+my $res = $mech->get($path_to_404);
+ok !$res->is_success(), "want a bad response";
+is $res->code, 404, "got 404";
+$mech->content_contains($path_to_404);
diff --git a/t/app/controller/questionnaire.t b/t/app/controller/questionnaire.t
new file mode 100644
index 000000000..e56734bfc
--- /dev/null
+++ b/t/app/controller/questionnaire.t
@@ -0,0 +1,293 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use FixMyStreet::App::Controller::Questionnaire;
+
+ok( my $mech = FixMyStreet::TestMech->new, 'Created mech object' );
+
+# create a test user and report
+$mech->delete_user('test@example.com');
+
+my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test@example.com', name => 'Test User' } );
+ok $user, "created test user";
+
+my $report_time = '2011-03-01 12:00:00';
+
+my $report = FixMyStreet::App->model('DB::Problem')->find_or_create(
+ {
+ postcode => 'EH1 1BB',
+ council => '2651',
+ areas => ',11808,135007,14419,134935,2651,20728,',
+ category => 'Street lighting',
+ title => 'Testing',
+ detail => 'Testing Detail',
+ used_map => 1,
+ name => $user->name,
+ anonymous => 0,
+ state => 'confirmed',
+ confirmed => $report_time,
+ lastupdate => $report_time,
+ whensent => '2011-03-01 12:05:00',
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 1,
+ latitude => '55.951963',
+ longitude => '-3.189944',
+ user_id => $user->id,
+ }
+);
+my $report_id = $report->id;
+ok $report, "created test report - $report_id";
+
+# Call the questionaire sending function...
+FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( {
+ site => 'fixmystreet'
+} );
+my $email = $mech->get_email;
+ok $email, "got an email";
+like $email->body, qr/fill in our short questionnaire/i, "got questionnaire email";
+my ($token) = $email->body =~ m{http://.*?/Q/(\S+)};
+ok $token, "extracted questionnaire token '$token'";
+$mech->clear_emails_ok;
+
+$report->discard_changes;
+is $report->send_questionnaire, 0;
+
+$token = FixMyStreet::App->model("DB::Token")->find( {
+ scope => 'questionnaire', token => $token
+} );
+ok $token, 'found token for questionnaire';
+
+my $questionnaire = FixMyStreet::App->model('DB::Questionnaire')->find( {
+ id => $token->data
+} );
+ok $questionnaire, 'found questionnaire';
+
+foreach my $test (
+ {
+ desc => 'User goes to questionnaire URL with a bad token',
+ token_extra => 'BAD',
+ content => "we couldn't validate that token",
+ },
+ {
+ desc => 'User goes to questionnaire URL for a now-hidden problem',
+ state => 'hidden',
+ content => "we couldn't locate your problem",
+ },
+ {
+ desc => 'User goes to questionnaire URL for an already answered questionnaire',
+ answered => \'ms_current_timestamp()',
+ content => 'already answered this questionnaire',
+ },
+) {
+ subtest $test->{desc} => sub {
+ $report->state( $test->{state} || 'confirmed' );
+ $report->update;
+ $questionnaire->whenanswered( $test->{answered} );
+ $questionnaire->update;
+ (my $token = $token->token);
+ $token .= $test->{token_extra} if $test->{token_extra};
+ $mech->get_ok("/Q/$token");
+ $mech->content_contains( $test->{content} );
+ # Reset, no matter what test did
+ $report->state( 'confirmed' );
+ $report->update;
+ $questionnaire->whenanswered( undef );
+ $questionnaire->update;
+ };
+}
+
+$mech->get_ok("/Q/" . $token->token);
+$mech->title_like( qr/Questionnaire/ );
+$mech->submit_form_ok( );
+my @errors = @{ $mech->page_errors };
+ok scalar @errors, 'displayed error messages';
+is $errors[0], "Please state whether or not the problem has been fixed", 'error message';
+
+foreach my $test (
+ {
+ desc => 'Open report, has been fixed, first time reporter, no update left',
+ problem_state => 'confirmed',
+ fields => {
+ been_fixed => 'Yes',
+ reported => 'No',
+ },
+ comment => 'Questionnaire filled in by problem reporter',
+ },
+ {
+ desc => 'Open report, has been fixed, reported before, leaves an update',
+ problem_state => 'confirmed',
+ fields => {
+ been_fixed => 'Yes',
+ reported => 'Yes',
+ update => 'The council fixed this really quickly, thanks!',
+ },
+ },
+ {
+ desc => 'Open report, has not been fixed, not reported before, no update, asks for another questionnaire',
+ problem_state => 'confirmed',
+ fields => {
+ been_fixed => 'No',
+ reported => 'No',
+ another => 'Yes',
+ },
+ },
+ {
+ desc => 'Open report, unknown fixed, reported before, update, no further questionnaire',
+ problem_state => 'confirmed',
+ fields => {
+ been_fixed => 'Unknown',
+ reported => 'Yes',
+ update => 'This is still going on.',
+ # another => 'No', Error for not setting this tested below
+ },
+ },
+ {
+ desc => 'Fixed report, confirmed fixed, not reported before, no update',
+ problem_state => 'fixed',
+ fields => {
+ been_fixed => 'Yes',
+ reported => 'No',
+ },
+ lastupdate_static => 1,
+ },
+ {
+ desc => 'Fixed report, unknown fixed, not reported before, no update, asks for another',
+ problem_state => 'fixed',
+ fields => {
+ been_fixed => 'Unknown',
+ reported => 'No',
+ another => 'Yes',
+ },
+ },
+ {
+ desc => 'Fixed report, reopened, reported before, no update, no further questionnaire',
+ problem_state => 'fixed',
+ fields => {
+ been_fixed => 'No',
+ reported => 'Yes',
+ another => 'No',
+ # update => 'Dummy', Error for not setting this tested below
+ },
+ },
+) {
+ subtest $test->{desc} => sub {
+ $report->state ( $test->{problem_state} );
+ $report->update;
+
+ $mech->get_ok("/Q/" . $token->token);
+ $mech->title_like( qr/Questionnaire/ );
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+
+ # If reopening, we've just submitted without an update. Should cause an error.
+ if ($test->{problem_state} eq 'fixed' && $test->{fields}{been_fixed} eq 'No') {
+ my @errors = @{ $mech->page_errors };
+ ok scalar @errors, 'displayed error messages';
+ is $errors[0], "Please provide some explanation as to why you're reopening this report", 'error message';
+ $test->{fields}{update} = 'This has not been fixed.';
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+ }
+
+ # We forgot to say we wanted another questionnaire or not with this test
+ if ($test->{problem_state} eq 'confirmed' && $test->{fields}{been_fixed} eq 'Unknown') {
+ my @errors = @{ $mech->page_errors };
+ ok scalar @errors, 'displayed error messages';
+ is $errors[0], "Please indicate whether you'd like to receive another questionnaire", 'error message';
+ $test->{fields}{another} = 'No';
+ $mech->submit_form_ok( { with_fields => $test->{fields} } );
+ }
+
+ my $result;
+ $result = 'fixed' if $test->{fields}{been_fixed} eq 'Yes';
+ $result = 'confirmed' if $test->{fields}{been_fixed} eq 'No';
+ $result = 'unknown' if $test->{fields}{been_fixed} eq 'Unknown';
+
+ my $another = 0;
+ $another = 1 if $test->{fields}{another} && $test->{fields}{another} eq 'Yes';
+
+ # Check the right HTML page has been returned
+ $mech->content_like( qr/<title>[^<]*Questionnaire/m );
+ $mech->content_contains( 'glad to hear it&rsquo;s been fixed' )
+ if $result eq 'fixed';
+ $mech->content_contains( 'get some more information about the status of your problem' )
+ if $result eq 'unknown';
+ $mech->content_contains( "sorry to hear that" )
+ if $result eq 'confirmed';
+
+ # Check the database has the right information
+ $report->discard_changes;
+ $questionnaire->discard_changes;
+ is $report->state, $result eq 'unknown' ? $test->{problem_state} : $result;
+ is $report->send_questionnaire, $another;
+ ok DateTime::Format::Pg->format_datetime( $report->lastupdate) gt $report_time, 'lastupdate changed'
+ unless $test->{fields}{been_fixed} eq 'Unknown' || $test->{lastupdate_static};
+ is $questionnaire->old_state, $test->{problem_state};
+ is $questionnaire->new_state, $result;
+ is $questionnaire->ever_reported, $test->{fields}{reported} eq 'Yes' ? 1 : 0;
+ if ($test->{fields}{update} || $test->{comment}) {
+ my $c = FixMyStreet::App->model("DB::Comment")->find(
+ { problem_id => $report->id }
+ );
+ is $c->text, $test->{fields}{update} || $test->{comment};
+ }
+
+ # Reset questionnaire for next test
+ $questionnaire->old_state( undef );
+ $questionnaire->new_state( undef );
+ $questionnaire->ever_reported( undef );
+ $questionnaire->whenanswered( undef );
+ $questionnaire->update;
+ $report->send_questionnaire( 0 );
+ $report->lastupdate( $report_time );
+ $report->comments->delete;
+ $report->update;
+ };
+}
+
+# EHA extra checking
+ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes';
+
+# Reset, and all the questionaire sending function - FIXME should it detect site itself somehow?
+$report->send_questionnaire( 1 );
+$report->update;
+$questionnaire->delete;
+FixMyStreet::App->model('DB::Questionnaire')->send_questionnaires( {
+ site => 'emptyhomes'
+} );
+$email = $mech->get_email;
+ok $email, "got an email";
+like $email->body, qr/fill in this short questionnaire/i, "got questionnaire email";
+($token) = $email->body =~ m{http://.*?/Q/(\S+)};
+ok $token, "extracted questionnaire token '$token'";
+
+$mech->get_ok("/Q/" . $token);
+$mech->content_contains( 'should have reported what they have done' );
+
+# Test already answered the ever reported question, so not shown again
+my $questionnaire2 = FixMyStreet::App->model('DB::Questionnaire')->find_or_create(
+ {
+ problem_id => $report->id,
+ whensent => '2011-03-28 12:00:00',
+ ever_reported => 1,
+ }
+);
+ok $questionnaire, 'added another questionnaire';
+ok $mech->host("fixmystreet.com"), 'change host to fixmystreet';
+$mech->get_ok("/Q/" . $token);
+$mech->title_like( qr/Questionnaire/ );
+$mech->content_contains( 'Has this problem been fixed?' );
+$mech->content_lacks( 'ever reported' );
+
+# EHA extra checking
+ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes';
+$mech->get_ok("/Q/" . $token);
+$mech->content_contains( 'made a lot of progress' );
+
+$mech->delete_user('test@example.com');
+done_testing();
diff --git a/t/app/controller/report_display.t b/t/app/controller/report_display.t
new file mode 100644
index 000000000..1f857a387
--- /dev/null
+++ b/t/app/controller/report_display.t
@@ -0,0 +1,265 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use Web::Scraper;
+use Path::Class;
+use DateTime;
+
+my $mech = FixMyStreet::TestMech->new;
+
+# create a test user and report
+$mech->delete_user('test@example.com');
+my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test@example.com', name => 'Test User' } );
+ok $user, "created test user";
+
+my $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',
+ council => '2504',
+ areas => ',105255,11806,11828,2247,2504,',
+ category => 'Other',
+ title => 'Test 2',
+ detail => 'Test 2 Detail',
+ used_map => 't',
+ name => 'Test User',
+ anonymous => 'f',
+ state => 'confirmed',
+ confirmed => $dt->ymd . ' ' . $dt->hms,
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 't',
+ latitude => '51.5016605453401',
+ longitude => '-0.142497580865087',
+ user_id => $user->id,
+ }
+);
+my $report_id = $report->id;
+ok $report, "created test report - $report_id";
+
+subtest "check that no id redirects to homepage" => sub {
+ $mech->get_ok('/report');
+ is $mech->uri->path, '/', "at home page";
+};
+
+subtest "test id=NNN redirects to /NNN" => sub {
+ $mech->get_ok("/report?id=$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+};
+
+subtest "test bad council email clients web links" => sub {
+ $mech->get_ok("/report/3D$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+};
+
+subtest "test tailing non-ints get stripped" => sub {
+ $mech->get_ok("/report/${report_id}xx ");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+};
+
+subtest "test bad ids get dealt with (404)" => sub {
+ foreach my $id ( 'XXX', 99999999 ) {
+ ok $mech->get("/report/$id"), "get '/report/$id'";
+ is $mech->res->code, 404, "page not found";
+ is $mech->uri->path, "/report/$id", "at /report/$id";
+ $mech->content_contains('Unknown problem ID');
+ }
+};
+
+subtest "change report to unconfirmed and check for 404 status" => sub {
+ ok $report->update( { state => 'unconfirmed' } ), 'unconfirm report';
+ ok $mech->get("/report/$report_id"), "get '/report/$report_id'";
+ is $mech->res->code, 404, "page not found";
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ $mech->content_contains('Unknown problem ID');
+ ok $report->update( { state => 'confirmed' } ), 'confirm report again';
+};
+
+subtest "change report to hidden and check for 410 status" => sub {
+ ok $report->update( { state => 'hidden' } ), 'hide report';
+ ok $mech->get("/report/$report_id"), "get '/report/$report_id'";
+ is $mech->res->code, 410, "page gone";
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ $mech->content_contains('That report has been removed from FixMyStreet.');
+ ok $report->update( { state => 'confirmed' } ), 'confirm report again';
+};
+
+subtest "test a good report" => sub {
+ $mech->get_ok("/report/$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ is $mech->extract_problem_title, 'Test 2', 'problem title';
+ is $mech->extract_problem_meta,
+ 'Reported by Test User at 15:47, Saturday 16 April 2011',
+ 'correct problem meta information';
+ $mech->content_contains('Test 2 Detail');
+
+ my $update_form = $mech->form_name('updateForm');
+
+ my %fields = (
+ name => '',
+ rznvy => '',
+ update => '',
+ add_alert => 1, # defaults to true
+ fixed => undef
+ );
+ is $update_form->value($_), $fields{$_}, "$_ value" for keys %fields;
+};
+
+foreach my $meta (
+ {
+ anonymous => 'f',
+ category => 'Other',
+ service => '',
+ meta => 'Reported by Test User at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 'f',
+ category => 'Roads',
+ service => '',
+ meta =>
+'Reported in the Roads category by Test User at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 'f',
+ category => '',
+ service => 'Transport service',
+ meta =>
+'Reported by Transport service by Test User at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 'f',
+ category => 'Roads',
+ service => 'Transport service',
+ meta =>
+'Reported by Transport service in the Roads category by Test User at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 't',
+ category => 'Other',
+ service => '',
+ meta => 'Reported anonymously at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 't',
+ category => 'Roads',
+ service => '',
+ meta =>
+'Reported in the Roads category anonymously at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 't',
+ category => '',
+ service => 'Transport service',
+ meta =>
+'Reported by Transport service anonymously at 15:47, Saturday 16 April 2011'
+ },
+ {
+ anonymous => 't',
+ category => 'Roads',
+ service => 'Transport service',
+ meta =>
+'Reported by Transport service in the Roads category anonymously at 15:47, Saturday 16 April 2011'
+ },
+ )
+{
+ $report->service( $meta->{service} );
+ $report->category( $meta->{category} );
+ $report->anonymous( $meta->{anonymous} );
+ $report->update;
+ subtest "test correct problem meta information" => sub {
+ $mech->get_ok("/report/$report_id");
+
+ is $mech->extract_problem_meta, $meta->{meta};
+
+ };
+}
+
+for my $test (
+ {
+ description => 'new report',
+ date => DateTime->now,
+ state => 'confirmed',
+ banner_id => '',
+ banner_text => '',
+ fixed => 0
+ },
+ {
+ description => 'old report',
+ date => DateTime->new(
+ year => 2009,
+ month => 6,
+ day => 12,
+ hour => 9,
+ minute => 43,
+ second => 12
+ ),
+ state => 'confirmed',
+ banner_id => 'unknown',
+ banner_text => 'This problem is old and of unknown status.',
+ fixed => 0
+ },
+ {
+ description => 'old fixed report',
+ date => DateTime->new(
+ year => 2009,
+ month => 6,
+ day => 12,
+ hour => 9,
+ minute => 43,
+ second => 12
+ ),
+ state => 'fixed',
+ banner_id => 'fixed',
+ banner_text => 'This problem has been fixed.',
+ fixed => 1
+ },
+ {
+ description => 'fixed report',
+ date => DateTime->now,
+ state => 'fixed',
+ banner_id => 'fixed',
+ banner_text => 'This problem has been fixed.',
+ fixed => 1
+ },
+) {
+ subtest "banner for $test->{description}" => sub {
+ $report->confirmed( $test->{date}->ymd . ' ' . $test->{date}->hms );
+ $report->lastupdate( $test->{date}->ymd . ' ' . $test->{date}->hms );
+ $report->state( $test->{state} );
+ $report->update;
+
+ $mech->get_ok("/report/$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ my $banner = $mech->extract_problem_banner;
+ $banner->{text} =~ s/^ //g;
+ $banner->{text} =~ s/ $//g;
+
+ is $banner->{id}, $test->{banner_id}, 'banner id';
+ is $banner->{text}, $test->{banner_text}, 'banner text';
+
+ my $update_form = $mech->form_name( 'updateForm' );
+ if ( $test->{fixed} ) {
+ is $update_form->find_input( 'fixed' ), undef, 'problem is fixed';
+ } else {
+ ok $update_form->find_input( 'fixed' ), 'problem is not fixed';
+ }
+ };
+}
+
+# tidy up
+$mech->delete_user('test@example.com');
+done_testing();
diff --git a/t/app/controller/report_import.t b/t/app/controller/report_import.t
new file mode 100644
index 000000000..61ee28b88
--- /dev/null
+++ b/t/app/controller/report_import.t
@@ -0,0 +1,332 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use Web::Scraper;
+use Path::Class;
+
+my $mech = FixMyStreet::TestMech->new;
+$mech->get_ok('/import');
+
+my $sample_file = file(__FILE__)->parent->file("sample.jpg")->stringify;
+ok -e $sample_file, "sample file $sample_file exists";
+
+# submit an empty report to import - check we get all errors
+subtest "Test creating bad partial entries" => sub {
+
+ foreach my $test (
+ {
+ fields => { email => 'bob', },
+ errors => [
+ 'You must supply a service',
+ 'Please enter a subject',
+ 'Please enter your name',
+ 'Please enter a valid email',
+ 'Either a location or a photo must be provided.',
+ ],
+ },
+ {
+ fields => { email => 'bob@example.com' },
+ errors => [
+ 'You must supply a service',
+ 'Please enter a subject',
+ 'Please enter your name',
+ 'Either a location or a photo must be provided.',
+ ],
+ },
+ {
+ fields => { lat => 1, lon => 1, },
+ errors => [
+ 'You must supply a service',
+ 'Please enter a subject',
+ 'Please enter your name',
+ 'Please enter your email',
+'We had a problem with the supplied co-ordinates - outside the UK?',
+ ],
+ },
+ {
+ fields => { photo => $sample_file, },
+ errors => [
+ 'You must supply a service',
+ 'Please enter a subject',
+ 'Please enter your name',
+ 'Please enter your email',
+ ],
+ },
+ )
+ {
+ $mech->get_ok('/import');
+
+ $mech->submit_form_ok( #
+ { with_fields => $test->{fields} },
+ "fill in form"
+ );
+
+ is_deeply( $mech->import_errors, $test->{errors}, "expected errors" );
+ }
+
+};
+
+subtest "Submit a correct entry" => sub {
+
+ $mech->get_ok('/import');
+
+ $mech->submit_form_ok( #
+ {
+ with_fields => {
+ service => 'test-script',
+ name => 'Test User',
+ email => 'test@example.com',
+ subject => 'Test report',
+ detail => 'This is a test report',
+ photo => $sample_file,
+ }
+ },
+ "fill in form"
+ );
+
+ is_deeply( $mech->import_errors, [], "got no errors" );
+ is $mech->content, 'SUCCESS', "Got success response";
+
+ # check that we have received the email
+ $mech->email_count_is(1);
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+
+ my ($token_url) = $email->body =~ m{(http://\S+)};
+ ok $token_url, "Found a token url $token_url";
+
+ # go to the token url
+ $mech->get_ok($token_url);
+
+ # check that we are on '/around'
+ is $mech->uri->path, '/around', "sent to /around";
+
+ # check that we are not shown anything as we don't have a location yet
+ is_deeply $mech->visible_form_values, { pc => '' },
+ "check only pc field is shown";
+
+ $mech->submit_form_ok( #
+ { with_fields => { pc => 'SW1A 1AA' } },
+ "fill in postcode"
+ );
+
+ is $mech->uri->path, '/report/new', "sent to report page";
+
+ # check that fields are prefilled for us
+ is_deeply $mech->visible_form_values,
+ {
+ name => 'Test User',
+ title => 'Test report',
+ detail => 'This is a test report',
+ photo => '',
+ phone => '',
+ may_show_name => '1',
+ category => '-- Pick a category --',
+ },
+ "check imported fields are shown";
+
+ # Check photo present, and still there after map submission (testing bug #18)
+ $mech->content_contains( '<img align="right" src="/photo?id' );
+ $mech->content_contains('latitude" value="51.50101"', 'Check latitude');
+ $mech->content_contains('longitude" value="-0.141587"', 'Check longitude');
+ $mech->submit_form_ok(
+ {
+ button => 'tile_32742.21793',
+ x => 10,
+ y => 10,
+ },
+ "New map location"
+ );
+ $mech->content_contains( '<img align="right" src="/photo?id' );
+ $mech->content_contains('latitude" value="51.50519"', 'Check latitude');
+ $mech->content_contains('longitude" value="-0.142608"', 'Check longitude');
+
+ # check that fields haven't changed at all
+ is_deeply $mech->visible_form_values,
+ {
+ name => 'Test User',
+ title => 'Test report',
+ detail => 'This is a test report',
+ photo => '',
+ phone => '',
+ may_show_name => '1',
+ category => '-- Pick a category --',
+ },
+ "check imported fields are shown";
+
+ # change the details
+ $mech->submit_form_ok( #
+ {
+ with_fields => {
+ name => 'New Test User',
+ title => 'New Test report',
+ detail => 'This is a test report',
+ phone => '01234 567 890',
+ may_show_name => '1',
+ category => 'Street lighting',
+ }
+ },
+ "Update details and save"
+ );
+
+ # check that report has been created
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test@example.com' } );
+ ok $user, "Found a user";
+
+ my $report = $user->problems->first;
+ is $report->state, 'confirmed', 'is confirmed';
+ is $report->title, 'New Test report', 'title is correct';
+
+ $mech->delete_user($user);
+};
+
+subtest "Submit a correct entry (with location)" => sub {
+
+ $mech->get_ok('/import');
+
+ $mech->submit_form_ok( #
+ {
+ with_fields => {
+ service => 'test-script',
+ lat => '51.5010096115539', # SW1A 1AA
+ lon => '-0.141587067110009',
+ name => 'Test User ll',
+ email => 'test-ll@example.com',
+ subject => 'Test report ll',
+ detail => 'This is a test report ll',
+ photo => $sample_file,
+ }
+ },
+ "fill in form"
+ );
+
+ is_deeply( $mech->import_errors, [], "got no errors" );
+ is $mech->content, 'SUCCESS', "Got success response";
+
+ # check that we have received the email
+ $mech->email_count_is(1);
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+
+ my ($token_url) = $email->body =~ m{(http://\S+)};
+ ok $token_url, "Found a token url $token_url";
+
+ # go to the token url
+ $mech->get_ok($token_url);
+
+ # check that we are on '/report/new'
+ is $mech->uri->path, '/report/new', "sent to /report/new";
+
+ # check that fields are prefilled for us
+ is_deeply $mech->visible_form_values,
+ {
+ name => 'Test User ll',
+ title => 'Test report ll',
+ detail => 'This is a test report ll',
+ photo => '',
+ phone => '',
+ may_show_name => '1',
+ category => '-- Pick a category --',
+ },
+ "check imported fields are shown";
+
+ # change the details
+ $mech->submit_form_ok( #
+ {
+ with_fields => {
+ name => 'New Test User ll',
+ title => 'New Test report ll',
+ detail => 'This is a test report ll',
+ phone => '01234 567 890',
+ may_show_name => '1',
+ category => 'Street lighting',
+ }
+ },
+ "Update details and save"
+ );
+
+ # check that report has been created
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test-ll@example.com' } );
+ ok $user, "Found a user";
+
+ my $report = $user->problems->first;
+ is $report->state, 'confirmed', 'is confirmed';
+ is $report->title, 'New Test report ll', 'title is correct';
+
+ $mech->delete_user($user);
+};
+
+subtest "Submit a correct entry (with location) to cobrand" => sub {
+
+ skip( "Need 'fiksgatami' in ALLOWED_COBRANDS config", 8 )
+ unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{fiksgatami};
+ mySociety::MaPit::configure('http://mapit.nuug.no/');
+ ok $mech->host("fiksgatami.no"), 'change host to fiksgatami';
+
+ $mech->get_ok('/import');
+
+ $mech->submit_form_ok( #
+ {
+ with_fields => {
+ service => 'test-script',
+ lat => '59',
+ lon => '10',
+ name => 'Test User ll',
+ email => 'test-ll@example.com',
+ subject => 'Test report ll',
+ detail => 'This is a test report ll',
+ photo => $sample_file,
+ }
+ },
+ "fill in form"
+ );
+
+ is_deeply( $mech->import_errors, [], "got no errors" );
+ is $mech->content, 'SUCCESS', "Got success response";
+
+ # check that we have received the email
+ $mech->email_count_is(1);
+ my $email = $mech->get_email;
+ $mech->clear_emails_ok;
+
+ my ($token_url) = $email->body =~ m{(http://\S+)};
+ ok $token_url, "Found a token url $token_url";
+
+ # go to the token url
+ $mech->get_ok($token_url);
+
+ # check that we are on '/report/new'
+ is $mech->uri->path, '/report/new', "sent to /report/new";
+
+ # check that fields are prefilled for us
+ is_deeply $mech->visible_form_values,
+ {
+ name => 'Test User ll',
+ title => 'Test report ll',
+ detail => 'This is a test report ll',
+ photo => '',
+ phone => '',
+ may_show_name => '1',
+ },
+ "check imported fields are shown";
+
+ my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find( { email => 'test-ll@example.com' } );
+ ok $user, "Found a user";
+
+ my $report = $user->problems->first;
+ is $report->state, 'partial', 'is still partial';
+ is $report->title, 'Test report ll', 'title is correct';
+ is $report->lang, 'nb', 'language is correct';
+
+ $mech->delete_user($user);
+};
+
+done_testing();
diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t
new file mode 100644
index 000000000..f06c23501
--- /dev/null
+++ b/t/app/controller/report_new.t
@@ -0,0 +1,647 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use Web::Scraper;
+
+my $mech = FixMyStreet::TestMech->new;
+$mech->get_ok('/report/new');
+
+subtest "test that bare requests to /report/new get redirected" => sub {
+
+ $mech->get_ok('/report/new');
+ is $mech->uri->path, '/around', "went to /around";
+ is_deeply { $mech->uri->query_form }, {}, "query empty";
+
+ $mech->get_ok('/report/new?pc=SW1A%201AA');
+ is $mech->uri->path, '/around', "went to /around";
+ is_deeply { $mech->uri->query_form }, { pc => 'SW1A 1AA' },
+ "pc correctly transferred";
+};
+
+my %contact_params = (
+ confirmed => 1,
+ deleted => 0,
+ editor => 'Test',
+ whenedited => \'current_timestamp',
+ note => 'Created for test',
+);
+# Let's make some contacts to send things to!
+my $contact1 = FixMyStreet::App->model('DB::Contact')->find_or_create( {
+ %contact_params,
+ area_id => 2651, # Edinburgh
+ category => 'Street lighting',
+ email => 'highways@example.com',
+} );
+my $contact2 = FixMyStreet::App->model('DB::Contact')->find_or_create( {
+ %contact_params,
+ area_id => 2226, # Gloucestershire
+ category => 'Potholes',
+ email => 'potholes@example.com',
+} );
+my $contact3 = FixMyStreet::App->model('DB::Contact')->find_or_create( {
+ %contact_params,
+ area_id => 2326, # Cheltenham
+ category => 'Trees',
+ email => 'trees@example.com',
+} );
+ok $contact1, "created test contact 1";
+ok $contact2, "created test contact 2";
+ok $contact3, "created test contact 3";
+
+# test that the various bit of form get filled in and errors correctly
+# generated.
+foreach my $test (
+ {
+ msg => 'all fields empty',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => '',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => {},
+ errors => [
+ 'Please enter a subject',
+ 'Please enter some details',
+ 'Please enter your email',
+ 'Please enter your name',
+ ],
+ },
+ {
+ msg => 'may_show_name defaults to true',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => '',
+ may_show_name => undef,
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => { may_show_name => '1' },
+ errors => [
+ 'Please enter a subject',
+ 'Please enter some details',
+ 'Please enter your email',
+ 'Please enter your name',
+ ],
+ },
+ {
+ msg => 'may_show_name unchanged if name is present (stays false)',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => 'Bob Jones',
+ may_show_name => undef,
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => {},
+ errors => [
+ 'Please enter a subject',
+ 'Please enter some details',
+ 'Please enter your email',
+ ],
+ },
+ {
+ msg => 'may_show_name unchanged if name is present (stays true)',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => 'Bob Jones',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => {},
+ errors => [
+ 'Please enter a subject',
+ 'Please enter some details',
+ 'Please enter your email',
+ ],
+ },
+ {
+ msg => 'title and details tidied up',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => 'DOG SHIT ON WALLS',
+ detail => 'on this portakabin - more of a portaloo HEH!!',
+ photo => '',
+ name => 'Bob Jones',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => {
+ title => 'Dog poo on walls',
+ detail =>
+ 'On this [portable cabin] - more of a [portable loo] HEH!!',
+ },
+ errors => [ 'Please enter your email', ],
+ },
+ {
+ msg => 'name too short',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => 'Test title',
+ detail => 'Test detail',
+ photo => '',
+ name => 'DUDE',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => {},
+ errors => [
+ 'Please enter your email',
+'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box',
+ ],
+ },
+ {
+ msg => 'name is anonymous',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => 'Test title',
+ detail => 'Test detail',
+ photo => '',
+ name => 'anonymous',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => {},
+ errors => [
+ 'Please enter your email',
+'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box',
+ ],
+ },
+ {
+ msg => 'email invalid',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => 'Test title',
+ detail => 'Test detail',
+ photo => '',
+ name => 'Joe Smith',
+ may_show_name => '1',
+ email => 'not an email',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => { email => 'notanemail', },
+ errors => [ 'Please enter a valid email', ],
+ },
+ {
+ msg => 'cleanup title and detail',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => " Test title ",
+ detail => " first line \n\n second\nline\n\n ",
+ photo => '',
+ name => '',
+ may_show_name => '1',
+ email => '',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => {
+ title => 'Test title',
+ detail => "First line\n\nSecond line",
+ },
+ errors => [
+ 'Please enter your email',
+ 'Please enter your name',
+ ],
+ },
+ {
+ msg => 'clean up name and email',
+ pc => 'SW1A 1AA',
+ fields => {
+ title => '',
+ detail => '',
+ photo => '',
+ name => ' Bob Jones ',
+ may_show_name => '1',
+ email => ' BOB @ExAmplE.COM ',
+ phone => '',
+ category => 'Street lighting',
+ password_sign_in => '',
+ password_register => '',
+ remember_me => undef,
+ },
+ changes => {
+ name => 'Bob Jones',
+ email => 'bob@example.com',
+ },
+ errors => [ 'Please enter a subject', 'Please enter some details', ],
+ },
+ )
+{
+ subtest "check form errors where $test->{msg}" => sub {
+ $mech->get_ok('/around');
+
+ # submit initial pc form
+ $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } },
+ "submit location" );
+ is_deeply $mech->form_errors, [], "no errors for pc '$test->{pc}'";
+
+ # click through to the report page
+ $mech->follow_link_ok( { text => 'skip this step', },
+ "follow 'skip this step' link" );
+
+ # submit the main form
+ $mech->submit_form_ok( { with_fields => $test->{fields} },
+ "submit form" );
+
+ # check that we got the errors expected
+ is_deeply $mech->form_errors, $test->{errors}, "check errors";
+
+ # check that fields have changed as expected
+ my $new_values = {
+ %{ $test->{fields} }, # values added to form
+ %{ $test->{changes} }, # changes we expect
+ };
+ is_deeply $mech->visible_form_values, $new_values,
+ "values correctly changed";
+ };
+}
+
+foreach my $test (
+ {
+ desc => 'does not have an account',
+ user => 0,
+ },
+ {
+ desc => 'does have an account and is not signed in; does not sign in',
+ user => 1,
+ }
+) {
+ subtest "test report creation for a user who " . $test->{desc} => sub {
+ $mech->log_out_ok;
+ $mech->clear_emails_ok;
+
+ # check that the user does not exist
+ my $test_email = 'test-1@example.com';
+ if ($test->{user}) {
+ my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } );
+ ok $user, "test user does exist";
+ $user->problems->delete;
+ $user->name( 'Old Name' );
+ $user->password( 'old_password' );
+ $user->update;
+ } else {
+ ok !FixMyStreet::App->model('DB::User')->find( { email => $test_email } ),
+ "test user does not exist";
+ }
+
+ # submit initial pc form
+ $mech->get_ok('/around');
+ $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } },
+ "submit location" );
+
+ # click through to the report page
+ $mech->follow_link_ok( { text => 'skip this step', },
+ "follow 'skip this step' link" );
+
+ $mech->submit_form_ok(
+ {
+ button => 'submit_register',
+ with_fields => {
+ title => 'Test Report',
+ detail => 'Test report details.',
+ photo => '',
+ name => 'Joe Bloggs',
+ may_show_name => '1',
+ email => 'test-1@example.com',
+ phone => '07903 123 456',
+ category => 'Street lighting',
+ password_register => 'secret',
+ }
+ },
+ "submit good details"
+ );
+
+ # check that we got the errors expected
+ is_deeply $mech->form_errors, [], "check there were no errors";
+
+ # check that the user has been created/ not changed
+ my $user =
+ FixMyStreet::App->model('DB::User')->find( { email => $test_email } );
+ ok $user, "user found";
+ if ($test->{user}) {
+ ok $user->check_password('old_password'), 'password unchanged';
+ } else {
+ ok $user->check_password('secret'), 'password set correctly';
+ }
+
+ # find the report
+ my $report = $user->problems->first;
+ ok $report, "Found the report";
+
+ # check that the report is not available yet.
+ is $report->state, 'unconfirmed', "report not confirmed";
+ is $mech->get( '/report/' . $report->id )->code, 404, "report not found";
+
+ # Check the report has been assigned appropriately
+ is $report->council, 2651;
+
+ # receive token
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/confirm the problem/i, "confirm the problem";
+
+ my ($url) = $email->body =~ m{(http://\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ # confirm token
+ $mech->get_ok($url);
+ $report->discard_changes;
+ is $report->state, 'confirmed', "Report is now confirmed";
+
+ $mech->get_ok( '/report/' . $report->id );
+
+ if ($test->{user}) {
+ is $report->name, 'Joe Bloggs', 'name updated correctly';
+ ok $report->user->check_password('secret'), 'password updated correctly';
+ }
+
+ # check that the reporter has an alert
+ my $alert = FixMyStreet::App->model('DB::Alert')->find( {
+ user => $report->user,
+ alert_type => 'new_updates',
+ parameter => $report->id,
+ } );
+ ok $alert, "created new alert";
+
+ # user is created and logged in
+ $mech->logged_in_ok;
+
+ # cleanup
+ $mech->delete_user($user)
+ if $test->{user};
+ };
+}
+
+subtest "test report creation for a user who is signing in as they report" => sub {
+ $mech->log_out_ok;
+ $mech->clear_emails_ok;
+
+ # check that the user does not exist
+ my $test_email = 'test-2@example.com';
+
+ my $user = FixMyStreet::App->model('DB::User')->find_or_create( { email => $test_email } );
+ ok $user, "test user does exist";
+
+ # setup the user.
+ ok $user->update( {
+ name => 'Joe Bloggs',
+ phone => '01234 567 890',
+ password => 'secret2',
+ } ), "set user details";
+
+ # submit initial pc form
+ $mech->get_ok('/around');
+ $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB', } },
+ "submit location" );
+
+ # click through to the report page
+ $mech->follow_link_ok( { text => 'skip this step', },
+ "follow 'skip this step' link" );
+
+ $mech->submit_form_ok(
+ {
+ button => 'submit_sign_in',
+ with_fields => {
+ title => 'Test Report',
+ detail => 'Test report details.',
+ photo => '',
+ email => 'test-2@example.com',
+ password_sign_in => 'secret2',
+ category => 'Street lighting',
+ }
+ },
+ "submit good details"
+ );
+
+ # check that we got the errors expected
+ is_deeply $mech->form_errors, [
+ 'You have successfully signed in; please check and confirm your details are accurate:',
+ ], "check there were errors";
+
+ # Now submit with a name
+ $mech->submit_form_ok(
+ {
+ with_fields => {
+ name => 'Joe Bloggs',
+ }
+ },
+ "submit good details"
+ );
+
+ # find the report
+ my $report = $user->problems->first;
+ ok $report, "Found the report";
+
+ # check that we got redirected to /report/
+ is $mech->uri->path, "/report/" . $report->id, "redirected to report page";
+
+ # Check the report has been assigned appropriately
+ is $report->council, 2651;
+
+ # check that no emails have been sent
+ $mech->email_count_is(0);
+
+ # check report is confirmed and available
+ is $report->state, 'confirmed', "report is now confirmed";
+ $mech->get_ok( '/report/' . $report->id );
+
+ # check that the reporter has an alert
+ my $alert = FixMyStreet::App->model('DB::Alert')->find( {
+ user => $report->user,
+ alert_type => 'new_updates',
+ parameter => $report->id,
+ } );
+ ok $alert, "created new alert";
+
+ # user is created and logged in
+ $mech->logged_in_ok;
+
+ # cleanup
+ $mech->delete_user($user)
+};
+
+#### test report creation for user with account and logged in
+foreach my $test (
+ { category => 'Trees', council => 2326 },
+ { category => 'Potholes', council => 2226 },
+) {
+ subtest "test report creation for a user who is logged in" => sub {
+
+ # check that the user does not exist
+ my $test_email = 'test-2@example.com';
+
+ $mech->clear_emails_ok;
+ my $user = $mech->log_in_ok($test_email);
+
+ # setup the user.
+ ok $user->update(
+ {
+ name => 'Test User',
+ phone => '01234 567 890',
+ }
+ ),
+ "set users details";
+
+ # submit initial pc form
+ $mech->get_ok('/around');
+ $mech->submit_form_ok( { with_fields => { pc => 'GL50 2PR', } },
+ "submit location" );
+
+ # click through to the report page
+ $mech->follow_link_ok( { text => 'skip this step', },
+ "follow 'skip this step' link" );
+
+ # check that the fields are correctly prefilled
+ is_deeply(
+ $mech->visible_form_values,
+ {
+ title => '',
+ detail => '',
+ may_show_name => '1',
+ name => 'Test User',
+ phone => '01234 567 890',
+ photo => '',
+ category => '-- Pick a category --',
+ },
+ "user's details prefilled"
+ );
+
+ $mech->submit_form_ok(
+ {
+ with_fields => {
+ title => 'Test Report',
+ detail => 'Test report details.',
+ photo => '',
+ name => 'Joe Bloggs',
+ may_show_name => '1',
+ phone => '07903 123 456',
+ category => $test->{category},
+ }
+ },
+ "submit good details"
+ );
+
+ # find the report
+ my $report = $user->problems->first;
+ ok $report, "Found the report";
+
+ # Check the report has been assigned appropriately
+ is $report->council, $test->{council};
+
+ # check that we got redirected to /report/
+ is $mech->uri->path, "/report/" . $report->id, "redirected to report page";
+
+ # check that no emails have been sent
+ $mech->email_count_is(0);
+
+ # check report is confirmed and available
+ is $report->state, 'confirmed', "report is now confirmed";
+ $mech->get_ok( '/report/' . $report->id );
+
+ # check that the reporter has an alert
+ my $alert = FixMyStreet::App->model('DB::Alert')->find( {
+ user => $report->user,
+ alert_type => 'new_updates',
+ parameter => $report->id,
+ } );
+ ok $alert, "created new alert";
+
+ # user is still logged in
+ $mech->logged_in_ok;
+
+ # cleanup
+ $mech->delete_user($user);
+ };
+
+}
+
+#### test uploading an image
+
+#### test completing a partial report (eq flickr upload)
+
+#### possibly manual testing
+# create report without using map
+# create report by clicking on may with javascript off
+# create report with images off
+
+subtest "check that a lat/lon off coast leads to /around" => sub {
+ my $off_coast_latitude = 50.78301;
+ my $off_coast_longitude = -0.646929;
+
+ $mech->get_ok( #
+ "/report/new"
+ . "?latitude=$off_coast_latitude"
+ . "&longitude=$off_coast_longitude"
+ );
+
+ is $mech->uri->path, '/around', "redirected to '/around'";
+
+ is_deeply #
+ $mech->page_errors,
+ [ 'That spot does not appear to be covered by a council. If you have'
+ . ' tried to report an issue past the shoreline, for example, please'
+ . ' specify the closest point on land.' ], #
+ "Found location error";
+
+};
+
+$contact1->delete;
+$contact2->delete;
+$contact3->delete;
+
+done_testing();
diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t
new file mode 100644
index 000000000..3076a4564
--- /dev/null
+++ b/t/app/controller/report_updates.t
@@ -0,0 +1,1030 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FixMyStreet::TestMech;
+use Web::Scraper;
+use Path::Class;
+use DateTime;
+
+my $mech = FixMyStreet::TestMech->new;
+
+# create a test user and report
+$mech->delete_user('commenter@example.com');
+$mech->delete_user('test@example.com');
+
+my $user =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'test@example.com', name => 'Test User' } );
+ok $user, "created test user";
+
+my $user2 =
+ FixMyStreet::App->model('DB::User')
+ ->find_or_create( { email => 'commenter@example.com', name => 'Commenter' } );
+ok $user2, "created comment user";
+
+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',
+ council => '2504',
+ areas => ',105255,11806,11828,2247,2504,',
+ category => 'Other',
+ title => 'Test 2',
+ detail => 'Test 2 Detail',
+ used_map => 't',
+ name => 'Test User',
+ anonymous => 'f',
+ state => 'confirmed',
+ confirmed => $dt->ymd . ' ' . $dt->hms,
+ lang => 'en-gb',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ send_questionnaire => 't',
+ latitude => '51.5016605453401',
+ longitude => '-0.142497580865087',
+ user_id => $user->id,
+ }
+);
+my $report_id = $report->id;
+ok $report, "created test report - $report_id";
+
+my $comment = FixMyStreet::App->model('DB::Comment')->find_or_create(
+ {
+ problem_id => $report_id,
+ user_id => $user2->id,
+ name => 'Other User',
+ mark_fixed => 'false',
+ text => 'This is some update text',
+ state => 'confirmed',
+ confirmed => $dt->ymd . ' ' . $dt->hms,
+ anonymous => 'f',
+ }
+);
+
+my $comment_id = $comment->id;
+ok $comment, "created test update - $comment_id";
+
+for my $test (
+ {
+ description => 'named user, anon is false',
+ name => 'Other User',
+ anonymous => 'f',
+ mark_fixed => 'false',
+ mark_open => 'false',
+ meta => 'Posted by Other User at 15:47, Saturday 16 April 2011',
+ },
+ {
+ description => 'blank user, anon is false',
+ name => '',
+ anonymous => 'f',
+ mark_fixed => 'false',
+ mark_open => 'false',
+ meta => 'Posted anonymously at 15:47, Saturday 16 April 2011',
+ },
+ {
+ description => 'named user, anon is true',
+ name => 'Other User',
+ anonymous => 't',
+ mark_fixed => 'false',
+ mark_open => 'false',
+ meta => 'Posted anonymously at 15:47, Saturday 16 April 2011',
+ },
+ {
+ description => 'named user, anon is true, fixed',
+ name => 'Other User',
+ anonymous => 't',
+ mark_fixed => 'true',
+ mark_open => 'false',
+ meta =>
+'Posted anonymously at 15:47, Saturday 16 April 2011, marked as fixed',
+ },
+ {
+ description => 'named user, anon is true, reopened',
+ name => 'Other User',
+ anonymous => 't',
+ mark_fixed => 'false',
+ mark_open => 'true',
+ meta => 'Posted anonymously at 15:47, Saturday 16 April 2011, reopened',
+ }
+ )
+{
+ subtest "test update displayed for $test->{description}" => sub {
+ $comment->name( $test->{name} );
+ $comment->mark_fixed( $test->{mark_fixed} );
+ $comment->mark_open( $test->{mark_open} );
+ $comment->anonymous( $test->{anonymous} );
+ $comment->update;
+
+ $mech->get_ok("/report/$report_id");
+ is $mech->uri->path, "/report/$report_id", "at /report/$report_id";
+ $mech->content_contains('This is some update text');
+
+ my $meta = $mech->extract_update_metas;
+ is scalar @$meta, 1, 'number of updates';
+ is $meta->[0], $test->{meta};
+ };
+}
+
+subtest "unconfirmed updates not displayed" => sub {
+ $comment->state( 'unconfirmed' );
+ $comment->update;
+ $mech->get_ok("/report/$report_id");
+
+ my $meta = $mech->extract_update_metas;
+ is scalar @$meta, 0, 'update not displayed';
+};
+
+subtest "several updates shown in correct order" => sub {
+ for my $fields ( {
+ problem_id => $report_id,
+ user_id => $user2->id,
+ name => 'Other User',
+ mark_fixed => 'false',
+ text => 'First update',
+ state => 'confirmed',
+ confirmed => '2011-03-10 12:23:15',
+ anonymous => 'f',
+ },
+ {
+ problem_id => $report_id,
+ user_id => $user->id,
+ name => 'Main User',
+ mark_fixed => 'false',
+ text => 'Second update',
+ state => 'confirmed',
+ confirmed => '2011-03-10 12:23:16',
+ anonymous => 'f',
+ },
+ {
+ problem_id => $report_id,
+ user_id => $user->id,
+ name => 'Other User',
+ anonymous => 'true',
+ mark_fixed => 'true',
+ text => 'Third update',
+ state => 'confirmed',
+ confirmed => '2011-03-15 08:12:36',
+ }
+ ) {
+ my $comment = FixMyStreet::App->model('DB::Comment')->find_or_create(
+ $fields
+ );
+ }
+
+ $mech->get_ok("/report/$report_id");
+
+ my $meta = $mech->extract_update_metas;
+ is scalar @$meta, 3, 'number of updates';
+ is $meta->[0], 'Posted by Other User at 12:23, Thursday 10 March 2011', 'first update';
+ is $meta->[1], 'Posted by Main User at 12:23, Thursday 10 March 2011', 'second update';
+ is $meta->[2], 'Posted anonymously at 08:12, Tuesday 15 March 2011, marked as fixed', 'third update';
+};
+
+for my $test (
+ {
+ desc => 'No email, no message',
+ fields => {
+ rznvy => '',
+ update => '',
+ name => '',
+ photo => '',
+ fixed => undef,
+ add_alert => 1,
+ may_show_name => undef,
+ remember_me => undef,
+ password_register => '',
+ password_sign_in => '',
+ },
+ changes => {},
+ field_errors => [ 'Please enter a message', 'Please enter your email', 'Please enter your name' ]
+ },
+ {
+ desc => 'Invalid email, no message',
+ fields => {
+ rznvy => 'test',
+ update => '',
+ name => '',
+ photo => '',
+ fixed => undef,
+ add_alert => 1,
+ may_show_name => undef,
+ remember_me => undef,
+ password_sign_in => '',
+ password_register => '',
+ },
+ changes => {},
+ field_errors => [ 'Please enter a message', 'Please enter a valid email', 'Please enter your name' ]
+ },
+ {
+ desc => 'email with spaces, no message',
+ fields => {
+ rznvy => 'test @ example. com',
+ update => '',
+ name => '',
+ photo => '',
+ fixed => undef,
+ add_alert => 1,
+ may_show_name => undef,
+ remember_me => undef,
+ password_register => '',
+ password_sign_in => '',
+ },
+ changes => {
+ rznvy => 'test@example.com',
+ },
+ field_errors => [ 'Please enter a message', 'Please enter your name' ]
+ },
+ {
+ desc => 'email with uppercase, no message',
+ fields => {
+ rznvy => 'test@EXAMPLE.COM',
+ update => '',
+ name => '',
+ photo => '',
+ fixed => undef,
+ add_alert => 1,
+ may_show_name => undef,
+ remember_me => undef,
+ password_register => '',
+ password_sign_in => '',
+ },
+ changes => {
+ rznvy => 'test@example.com',
+ },
+ field_errors => [ 'Please enter a message', 'Please enter your name' ]
+ },
+ )
+{
+ subtest "submit an update - $test->{desc}" => sub {
+ $mech->get_ok("/report/$report_id");
+
+ $mech->submit_form_ok( { with_fields => $test->{fields} },
+ 'submit update' );
+
+ is_deeply $mech->form_errors, $test->{field_errors}, 'field errors';
+
+ my $values = {
+ %{ $test->{fields} },
+ %{ $test->{changes} },
+ };
+
+ is_deeply $mech->visible_form_values('updateForm'), $values, 'form changes';
+ };
+}
+
+for my $test (
+ {
+ desc => 'submit an update for a non registered user',
+ initial_values => {
+ name => '',
+ rznvy => '',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ remember_me => undef,
+ password_register => '',
+ password_sign_in => '',
+ },
+ form_values => {
+ submit_update => 1,
+ rznvy => 'unregistered@example.com',
+ update => 'Update from an unregistered user',
+ add_alert => undef,
+ name => 'Unreg User',
+ may_show_name => undef,
+ },
+ changes => {},
+ },
+ {
+ desc => 'submit an update for a non registered user and sign up',
+ initial_values => {
+ name => '',
+ rznvy => '',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ remember_me => undef,
+ password_register => '',
+ password_sign_in => '',
+ },
+ form_values => {
+ submit_update => 1,
+ rznvy => 'unregistered@example.com',
+ update => 'update from an unregistered user',
+ add_alert => 1,
+ name => 'Unreg User',
+ may_show_name => undef,
+ },
+ changes => {
+ update => 'Update from an unregistered user',
+ },
+ }
+) {
+ subtest $test->{desc} => sub {
+ $mech->log_out_ok();
+ $mech->clear_emails_ok();
+
+ $mech->get_ok("/report/$report_id");
+
+ my $values = $mech->visible_form_values('updateForm');
+
+ is_deeply $values, $test->{initial_values}, 'initial form values';
+
+ $mech->submit_form_ok(
+ {
+ with_fields => $test->{form_values}
+ },
+ 'submit update'
+ );
+
+ $mech->content_contains('Nearly Done! Now check your email');
+
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/confirm the update you/i, "Correct email text";
+
+ my ( $url, $url_token ) = $email->body =~ m{(http://\S+/C/)(\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ my $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'comment'
+ }
+ );
+ ok $token, 'Token found in database';
+
+ my $update_id = $token->data->{id};
+ my $add_alerts = $token->data->{add_alert};
+ my $update =
+ FixMyStreet::App->model('DB::Comment')->find( { id => $update_id } );
+
+ my $details = {
+ %{ $test->{form_values} },
+ %{ $test->{changes} }
+ };
+
+ ok $update, 'found update in database';
+ is $update->state, 'unconfirmed', 'update unconfirmed';
+ is $update->user->email, $details->{rznvy}, 'update email';
+ is $update->text, $details->{update}, 'update text';
+ is $add_alerts, $details->{add_alert} ? 1 : 0, 'do not sign up for alerts';
+
+ $mech->get_ok( $url . $url_token );
+ $mech->content_contains("/report/$report_id#update_$update_id");
+
+ my $unreg_user = FixMyStreet::App->model( 'DB::User' )->find( { email => $details->{rznvy} } );
+
+ ok $unreg_user, 'found user';
+
+ my $alert = FixMyStreet::App->model( 'DB::Alert' )->find(
+ { user => $unreg_user, alert_type => 'new_updates', confirmed => 1, }
+ );
+
+ ok $details->{add_alert} ? defined( $alert ) : !defined( $alert ), 'sign up for alerts';
+
+ $update->discard_changes;
+
+ is $update->state, 'confirmed', 'update confirmed';
+ $mech->delete_user( $unreg_user );
+ };
+}
+
+for my $test (
+ {
+ desc => 'submit an update for a registered user, signing in with wrong password',
+ form_values => {
+ submit_update => 1,
+ rznvy => 'registered@example.com',
+ update => 'Update from a user',
+ add_alert => undef,
+ password_sign_in => 'secret',
+ },
+ field_errors => [
+ "There was a problem with your email/password combination. Passwords and user accounts are a brand new service, so you probably do not have one yet \x{2013} please fill in the right hand side of this form to get one.",
+ 'Please enter your name', # FIXME Not really necessary error
+ ],
+ },
+ {
+ desc => 'submit an update for a registered user and sign in',
+ form_values => {
+ submit_update => 1,
+ rznvy => 'registered@example.com',
+ update => 'Update from a user',
+ add_alert => undef,
+ password_sign_in => 'secret2',
+ },
+ field_errors => [
+ 'You have successfully signed in; please check and confirm your details are accurate:',
+ ],
+ }
+) {
+ subtest $test->{desc} => sub {
+ # Set things up
+ my $user = $mech->create_user_ok( $test->{form_values}->{rznvy} );
+ my $pw = 'secret2';
+ $user->update( { name => 'Mr Reg', password => $pw } );
+ $report->comments->delete;
+
+ $mech->log_out_ok();
+ $mech->clear_emails_ok();
+ $mech->get_ok("/report/$report_id");
+ $mech->submit_form_ok(
+ {
+ button => 'submit_sign_in',
+ with_fields => $test->{form_values}
+ },
+ 'submit update'
+ );
+
+ is_deeply $mech->form_errors, $test->{field_errors}, 'check there were errors';
+
+ SKIP: {
+ skip( "Incorrect password", 5 ) unless $test->{form_values}{password_sign_in} eq $pw;
+
+ # Now submit with a name
+ $mech->submit_form_ok(
+ {
+ with_fields => {
+ name => 'Joe Bloggs',
+ }
+ },
+ "submit good details"
+ );
+
+ is $mech->uri->path, "/report/" . $report_id, "redirected to report page";
+ $mech->email_count_is(0);
+
+ my $update = $report->comments->first;
+ ok $update, 'found update';
+ is $update->text, $test->{form_values}->{update}, 'update text';
+ is $update->user->email, $test->{form_values}->{rznvy}, 'update user';
+ is $update->state, 'confirmed', 'update confirmed';
+ $mech->delete_user( $update->user );
+ }
+ };
+}
+
+subtest 'submit an update for a registered user, creating update by email' => sub {
+ my $user = $mech->create_user_ok( 'registered@example.com' );
+ $user->update( { name => 'Mr Reg', password => 'secret2' } );
+ $report->comments->delete;
+ $mech->log_out_ok();
+ $mech->clear_emails_ok();
+ $mech->get_ok("/report/$report_id");
+ $mech->submit_form_ok( {
+ with_fields => {
+ submit_update => 1,
+ rznvy => 'registered@example.com',
+ update => 'Update from a user',
+ add_alert => undef,
+ name => 'New Name',
+ password_register => 'new_secret',
+ },
+ }, 'submit update' );
+
+ $mech->content_contains('Nearly Done! Now check your email');
+
+ # No change to user yet.
+ $user->discard_changes;
+ ok $user->check_password( 'secret2' ), 'password unchanged';
+ is $user->name, 'Mr Reg', 'name unchanged';
+
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/confirm the update you/i, "Correct email text";
+
+ my ( $url, $url_token ) = $email->body =~ m{(http://\S+/C/)(\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ my $token = FixMyStreet::App->model('DB::Token')->find( {
+ token => $url_token,
+ scope => 'comment'
+ } );
+ ok $token, 'Token found in database';
+
+ my $update_id = $token->data->{id};
+ my $add_alerts = $token->data->{add_alert};
+ my $update = FixMyStreet::App->model('DB::Comment')->find( { id => $update_id } );
+
+ ok $update, 'found update in database';
+ is $update->state, 'unconfirmed', 'update unconfirmed';
+ is $update->user->email, 'registered@example.com', 'update email';
+ is $update->text, 'Update from a user', 'update text';
+
+ $mech->get_ok( $url . $url_token );
+ $mech->content_contains("/report/$report_id#update_$update_id");
+
+ # User should have new name and password
+ $user->discard_changes;
+ ok $user->check_password( 'new_secret' ), 'password changed';
+ is $user->name, 'New Name', 'name changed';
+
+ $update->discard_changes;
+ is $update->state, 'confirmed', 'update confirmed';
+ $mech->delete_user( $user );
+};
+
+for my $test (
+ {
+ desc => 'submit update for registered user',
+ initial_values => {
+ name => 'Test User',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ email => 'test@example.com',
+ fields => {
+ submit_update => 1,
+ update => 'update from a registered user',
+ add_alert => undef,
+ fixed => undef,
+ },
+ changed => {
+ update => 'Update from a registered user'
+ },
+ initial_banner => '',
+ endstate_banner => '',
+ alert => 0,
+ anonymous => 0,
+ },
+ {
+ desc => 'submit update for registered user anonymously by unchecking',
+ initial_values => {
+ name => 'Test User',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ email => 'test@example.com',
+ fields => {
+ submit_update => 1,
+ update => 'update from a registered user',
+ may_show_name => undef,
+ add_alert => undef,
+ fixed => undef,
+ },
+ changed => {
+ update => 'Update from a registered user'
+ },
+ initial_banner => '',
+ endstate_banner => '',
+ alert => 0,
+ anonymous => 1,
+ },
+ {
+ desc => 'submit update for registered user and sign up',
+ initial_values => {
+ name => 'Test User',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ email => 'test@example.com',
+ fields => {
+ submit_update => 1,
+ update => 'update from a registered user',
+ add_alert => 1,
+ fixed => undef,
+ },
+ changed => {
+ update => 'Update from a registered user'
+ },
+ initial_banner => '',
+ endstate_banner => '',
+ alert => 1,
+ anonymous => 0,
+ },
+ {
+ desc => 'submit update for registered user and mark fixed',
+ initial_values => {
+ name => 'Commenter',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ email => 'commenter@example.com',
+ fields => {
+ submit_update => 1,
+ update => 'update from a registered user',
+ add_alert => 1,
+ fixed => 1,
+ },
+ changed => {
+ update => 'Update from a registered user'
+ },
+ initial_banner => '',
+ endstate_banner => ' This problem has been fixed. ',
+ alert => 1,
+ anonymous => 0,
+ },
+ {
+ desc => 'submit another update for registered user and want no more alerts',
+ initial_values => {
+ name => 'Commenter',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ },
+ email => 'commenter@example.com',
+ fields => {
+ submit_update => 1,
+ update => 'another update from a registered user',
+ add_alert => undef,
+ },
+ changed => {
+ update => 'Another update from a registered user'
+ },
+ initial_banner => ' This problem has been fixed. ',
+ endstate_banner => ' This problem has been fixed. ',
+ alert => 0,
+ anonymous => 0,
+ },
+ # If logged in person unticks the box and already has an alert, they should be unsubscribed.
+) {
+ subtest $test->{desc} => sub {
+ $mech->log_out_ok();
+
+ # clear out comments for this problem to make
+ # checking details easier later
+ ok( $_->delete, 'deleted comment ' . $_->id )
+ for $report->comments;
+
+ $mech->clear_emails_ok();
+
+ $mech->log_in_ok( $test->{email} );
+ $mech->get_ok("/report/$report_id");
+
+ my $values = $mech->visible_form_values( 'updateForm' );
+
+ is_deeply $values, $test->{initial_values}, 'initial form values';
+
+ is $mech->extract_problem_banner->{text}, $test->{initial_banner}, 'initial banner';
+
+ $mech->submit_form_ok(
+ {
+ with_fields => $test->{fields},
+ },
+ 'submit update'
+ );
+
+ is $mech->uri->path, "/report/" . $report_id, "redirected to report page";
+
+ is $mech->extract_problem_banner->{text}, $test->{endstate_banner}, 'submitted banner';
+
+ $mech->email_count_is(0);
+
+ my $results = {
+ %{ $test->{fields} },
+ %{ $test->{changed} },
+ };
+
+ my $update = $report->comments->first;
+ ok $update, 'found update';
+ is $update->text, $results->{update}, 'update text';
+ is $update->user->email, $test->{email}, 'update user';
+ is $update->state, 'confirmed', 'update confirmed';
+ is $update->anonymous, $test->{anonymous}, 'user anonymous';
+
+ my $alert =
+ FixMyStreet::App->model('DB::Alert')
+ ->find( { user => $update->user, alert_type => 'new_updates', confirmed => 1, whendisabled => undef } );
+
+ ok $test->{alert} ? $alert : !$alert, 'not signed up for alerts';
+ };
+}
+
+foreach my $test (
+ {
+ desc => 'logged in reporter submits update and marks problem fixed',
+ initial_values => {
+ name => 'Test User',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ email => 'test@example.com',
+ fields => {
+ submit_update => 1,
+ update => 'update from owner',
+ add_alert => undef,
+ fixed => 1,
+ },
+ changed => { update => 'Update from owner' },
+ initial_banner => '',
+ alert => 1, # we signed up for alerts before, do not unsign us
+ anonymous => 0,
+ answered => 0,
+ path => '/report/update',
+ content =>
+"Thanks, glad to hear it's been fixed! Could we just ask if you have ever reported a problem to a council before?",
+ },
+ {
+ desc =>
+'logged in reporter submits update and marks problem fixed and has answered questionnaire',
+ initial_values => {
+ name => 'Test User',
+ may_show_name => 1,
+ add_alert => 1,
+ photo => '',
+ update => '',
+ fixed => undef,
+ },
+ email => 'test@example.com',
+ fields => {
+ submit_update => 1,
+ update => 'update from owner',
+ add_alert => undef,
+ fixed => 1,
+ },
+ changed => { update => 'Update from owner' },
+ initial_banner => '',
+ alert => 1, # we signed up for alerts before, do not unsign us
+ anonymous => 0,
+ answered => 1,
+ path => '/report/' . $report->id,
+ content => $report->title,
+ },
+ )
+{
+ subtest $test->{desc} => sub {
+
+ # double check
+ $mech->log_out_ok();
+
+ # clear out comments for this problem to make
+ # checking details easier later
+ ok( $_->delete, 'deleted comment ' . $_->id ) for $report->comments;
+
+ $report->discard_changes;
+ $report->state('confirmed');
+ $report->update;
+
+ my $questionnaire;
+ if ( $test->{answered} ) {
+ $questionnaire =
+ FixMyStreet::App->model('DB::Questionnaire')->create(
+ {
+ problem_id => $report_id,
+ ever_reported => 'y',
+ whensent => \'ms_current_timestamp()',
+ }
+ );
+
+ ok $questionnaire, 'added questionnaire';
+ }
+
+ $report->discard_changes;
+
+ $mech->clear_emails_ok();
+
+ $mech->log_in_ok( $test->{email} );
+ $mech->get_ok("/report/$report_id");
+
+ my $values = $mech->visible_form_values('updateForm');
+
+ is_deeply $values, $test->{initial_values}, 'initial form values';
+
+ is $mech->extract_problem_banner->{text}, $test->{initial_banner},
+ 'initial banner';
+
+ $mech->submit_form_ok( { with_fields => $test->{fields}, },
+ 'submit update' );
+
+ is $mech->uri->path, $test->{path}, "page after submission";
+
+ $mech->content_contains( $test->{content} );
+
+ $mech->email_count_is(0);
+
+ my $results = { %{ $test->{fields} }, %{ $test->{changed} }, };
+
+ my $update = $report->comments->first;
+ ok $update, 'found update';
+ is $update->text, $results->{update}, 'update text';
+ is $update->user->email, $test->{email}, 'update user';
+ is $update->state, 'confirmed', 'update confirmed';
+ is $update->anonymous, $test->{anonymous}, 'user anonymous';
+
+ SKIP: {
+ skip( 'not answering questionnaire', 5 ) if $questionnaire;
+
+ $mech->submit_form_ok( );
+
+ my @errors = @{ $mech->page_errors };
+ ok scalar @errors, 'displayed error messages';
+ is $errors[0], "Please say whether you've ever reported a problem to your council before", 'error message';
+
+ $mech->submit_form_ok( { with_fields => { reported => 'Yes' } } );
+
+ $mech->content_contains( 'Thank you &mdash; you can' );
+
+ $questionnaire = FixMyStreet::App->model( 'DB::Questionnaire' )->find(
+ { problem_id => $report_id }
+ );
+
+ ok $questionnaire, 'questionnaire exists';
+ ok $questionnaire->ever_reported, 'ever reported is yes';
+ };
+
+ if ($questionnaire) {
+ $questionnaire->delete;
+ ok !$questionnaire->in_storage, 'questionnaire deleted';
+ }
+ };
+}
+
+
+for my $test (
+ {
+ desc => 'reporter submits update and marks problem fixed',
+ fields => {
+ submit_update => 1,
+ name => 'Test User',
+ rznvy => 'test@example.com',
+ may_show_name => 1,
+ update => 'update from owner',
+ add_alert => undef,
+ fixed => 1,
+ },
+ changed => { update => 'Update from owner' },
+ initial_banner => '',
+ alert => 1, # we signed up for alerts before, do not unsign us
+ anonymous => 0,
+ answered => 0,
+ path => '/report/update',
+ content =>
+"Thanks, glad to hear it's been fixed! Could we just ask if you have ever reported a problem to a council before?",
+ },
+ {
+ desc =>
+'reporter submits update and marks problem fixed and has answered questionnaire',
+ fields => {
+ submit_update => 1,
+ name => 'Test User',
+ may_show_name => 1,
+ rznvy => 'test@example.com',
+ update => 'update from owner',
+ add_alert => undef,
+ fixed => 1,
+ },
+ changed => { update => 'Update from owner' },
+ initial_banner => '',
+ alert => 1, # we signed up for alerts before, do not unsign us
+ anonymous => 0,
+ answered => 1,
+ path => '/report/update',
+ content => "You have successfully confirmed your update",
+ },
+ )
+{
+ subtest $test->{desc} => sub {
+
+ # double check
+ $mech->log_out_ok();
+
+ # clear out comments for this problem to make
+ # checking details easier later
+ ok( $_->delete, 'deleted comment ' . $_->id ) for $report->comments;
+
+ $report->discard_changes;
+ $report->state('confirmed');
+ $report->update;
+
+ my $questionnaire;
+ if ( $test->{answered} ) {
+ $questionnaire =
+ FixMyStreet::App->model('DB::Questionnaire')->create(
+ {
+ problem_id => $report_id,
+ ever_reported => 'y',
+ whensent => \'ms_current_timestamp()',
+ }
+ );
+
+ ok $questionnaire, 'added questionnaire';
+ }
+
+ $report->discard_changes;
+
+ $mech->clear_emails_ok();
+
+ $mech->get_ok("/report/$report_id");
+
+ my $values = $mech->visible_form_values('updateForm');
+
+ is $mech->extract_problem_banner->{text}, $test->{initial_banner},
+ 'initial banner';
+
+ $mech->submit_form_ok( { with_fields => $test->{fields}, },
+ 'submit update' );
+
+ is $mech->uri->path, $test->{path}, "page after submission";
+
+ $mech->content_contains( 'Now check your email' );
+
+ $mech->email_count_is(1);
+
+ my $results = { %{ $test->{fields} }, %{ $test->{changed} }, };
+
+ my $update = $report->comments->first;
+ ok $update, 'found update';
+ is $update->text, $results->{update}, 'update text';
+ is $update->user->email, $test->{fields}->{rznvy}, 'update user';
+ is $update->state, 'unconfirmed', 'update confirmed';
+ is $update->anonymous, $test->{anonymous}, 'user anonymous';
+
+ my $email = $mech->get_email;
+ ok $email, "got an email";
+ like $email->body, qr/confirm the update you/i, "Correct email text";
+
+ my ( $url, $url_token ) = $email->body =~ m{(http://\S+/C/)(\S+)};
+ ok $url, "extracted confirm url '$url'";
+
+ my $token = FixMyStreet::App->model('DB::Token')->find(
+ {
+ token => $url_token,
+ scope => 'comment'
+ }
+ );
+ ok $token, 'Token found in database';
+
+ $mech->get_ok( '/C/' . $url_token );
+
+ $mech->content_contains( $test->{content} );
+
+ SKIP: {
+ skip( 'not answering questionnaire', 5 ) if $questionnaire;
+
+ $mech->submit_form_ok( );
+
+ my @errors = @{ $mech->page_errors };
+ ok scalar @errors, 'displayed error messages';
+ is $errors[0], "Please say whether you've ever reported a problem to your council before", 'error message';
+
+ $mech->submit_form_ok( { with_fields => { reported => 'Yes' } } );
+
+ $mech->content_contains( 'Thank you &mdash; you can' );
+
+ $questionnaire = FixMyStreet::App->model( 'DB::Questionnaire' )->find(
+ { problem_id => $report_id }
+ );
+
+ ok $questionnaire, 'questionnaire exists';
+ ok $questionnaire->ever_reported, 'ever reported is yes';
+ };
+
+ if ($questionnaire) {
+ $questionnaire->delete;
+ ok !$questionnaire->in_storage, 'questionnaire deleted';
+ }
+ };
+}
+
+subtest 'check have to be logged in for creator fixed questionnaire' => sub {
+ $mech->log_out_ok();
+
+ $mech->get_ok( "/questionnaire/submit?problem=$report_id&reported=Yes" );
+
+ $mech->content_contains( "I'm afraid we couldn't locate your problem in the database." )
+};
+
+subtest 'check cannot answer other user\'s creator fixed questionnaire' => sub {
+ $mech->log_out_ok();
+ $mech->log_in_ok( $user2->email );
+
+ $mech->get_ok( "/questionnaire/submit?problem=$report_id&reported=Yes" );
+
+ $mech->content_contains( "I'm afraid we couldn't locate your problem in the database." )
+};
+
+ok $comment->delete, 'deleted comment';
+$mech->delete_user('commenter@example.com');
+$mech->delete_user('test@example.com');
+done_testing();
diff --git a/t/app/controller/reports.t b/t/app/controller/reports.t
new file mode 100644
index 000000000..58803d778
--- /dev/null
+++ b/t/app/controller/reports.t
@@ -0,0 +1,37 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+use mySociety::MaPit;
+
+ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' );
+
+# Run the cron script that makes the data for /reports so we don't get an error.
+system( "bin/cron-wrapper update-all-reports" );
+
+# check that we can get the page
+$mech->get_ok('/reports');
+$mech->title_like(qr{Summary reports});
+$mech->content_contains('Birmingham');
+$mech->follow_link_ok( { text_regex => qr/Birmingham/ } );
+
+SKIP: {
+ skip( "Need 'emptyhomes' in ALLOWED_COBRANDS config", 8 )
+ unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{emptyhomes};
+ ok $mech->host("reportemptyhomes.com"), 'change host to reportemptyhomes';
+ $mech->get_ok('/reports');
+ # EHA lacks one column the others have
+ $mech->content_lacks('state unknown');
+
+ skip( "Need 'fiksgatami' in ALLOWED_COBRANDS config", 8 )
+ unless FixMyStreet::App->config->{ALLOWED_COBRANDS} =~ m{fiksgatami};
+ mySociety::MaPit::configure('http://mapit.nuug.no/');
+ ok $mech->host("fiksgatami.no"), 'change host to fiksgatami';
+ $mech->get_ok('/reports');
+ # There should only be one Oslo
+ $mech->content_contains('Oslo');
+ $mech->content_unlike(qr{Oslo">Oslo.*Oslo}s);
+}
+
+done_testing();
+
diff --git a/t/app/controller/sample.jpg b/t/app/controller/sample.jpg
new file mode 100644
index 000000000..23198cb83
--- /dev/null
+++ b/t/app/controller/sample.jpg
Binary files differ
diff --git a/t/app/helpers/send_email.t b/t/app/helpers/send_email.t
new file mode 100644
index 000000000..ac7e5b5c2
--- /dev/null
+++ b/t/app/helpers/send_email.t
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use utf8;
+
+BEGIN {
+ use FixMyStreet;
+ FixMyStreet->test_mode(1);
+}
+
+use Test::More tests => 5;
+
+use Email::Send::Test;
+use Path::Class;
+
+use_ok 'FixMyStreet::App';
+my $c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://fixmystreet.com/'),
+ uri => URI->new('http://fixmystreet.com/')
+ }
+ ),
+ }
+);
+$c->setup_request();
+
+# set some values in the stash
+$c->stash->{foo} = 'bar';
+
+# clear the email queue
+Email::Send::Test->clear;
+
+# send the test email
+ok $c->send_email( 'test.txt', { to => 'test@recipient.com' } ),
+ "sent an email";
+
+# check it got templated and sent correctly
+my @emails = Email::Send::Test->emails;
+is scalar(@emails), 1, "caught one email";
+
+# Get the email, check it has a date and then strip it out
+my $email_as_string = $emails[0]->as_string;
+ok $email_as_string =~ s{\s+Date:\s+\S.*?$}{}xms, "Found and stripped out date";
+
+my $expected_email_content = file(__FILE__)->dir->file('send_email_sample.txt')->slurp;
+my $name = FixMyStreet->config('CONTACT_NAME');
+$name = "\"$name\"" if $name =~ / /;
+my $sender = $name . ' <' . FixMyStreet->config('CONTACT_EMAIL') . '>';
+$expected_email_content =~ s{CONTACT_EMAIL}{$sender};
+
+is $email_as_string,
+$expected_email_content,
+ "email is as expected";
diff --git a/t/app/helpers/send_email_sample.txt b/t/app/helpers/send_email_sample.txt
new file mode 100644
index 000000000..2fe5272cb
--- /dev/null
+++ b/t/app/helpers/send_email_sample.txt
@@ -0,0 +1,29 @@
+MIME-Version: 1.0
+Subject: test email =?utf-8?Q?=E2=98=BA?=
+Content-Type: text/plain; charset="utf-8"
+To: test@recipient.com
+Content-Transfer-Encoding: quoted-printable
+From: CONTACT_EMAIL
+
+ Hello,
+
+ This is a test email where foo: bar.
+
+ utf8: =E6=88=91=E4=BB=AC=E5=BA=94=E8=AF=A5=E8=83=BD=E5=A4=9F=E6=97=A0=
+=E7=BC=9D=E5=A4=84=E7=90=86UTF8=E7=BC=96=E7=A0=81
+
+ indented_text
+
+ long line: Lorem ipsum dolor sit amet, consectetur adipisicing
+ elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud
+ exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate
+ velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
+ sint occaecat cupidatat non proident, sunt in culpa qui officia
+ deserunt mollit anim id est laborum.
+
+ Yours,=20=20
+ FixMyStreet.=20=
+
+
diff --git a/t/app/load_general_config.t b/t/app/load_general_config.t
new file mode 100644
index 000000000..3855c2565
--- /dev/null
+++ b/t/app/load_general_config.t
@@ -0,0 +1,13 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use_ok 'FixMyStreet::App';
+
+# GAZE_URL chosen as it is unlikely to change
+is FixMyStreet::App->config->{GAZE_URL}, #
+ 'http://gaze.mysociety.org/gaze', #
+ "check that known config param is loaded";
diff --git a/t/app/model/comment.t b/t/app/model/comment.t
new file mode 100644
index 000000000..93104c2e5
--- /dev/null
+++ b/t/app/model/comment.t
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use FixMyStreet;
+use FixMyStreet::App;
+
+my $comment_rs = FixMyStreet::App->model('DB::Comment');
+
+my $comment = $comment_rs->new(
+ {
+ user_id => 1,
+ problem_id => 1,
+ text => '',
+ state => 'confirmed',
+ anonymous => 0,
+ mark_fixed => 0,
+ cobrand => 'default',
+ cobrand_data => '',
+ }
+);
+
+is $comment->confirmed_local, undef, 'inflating null confirmed ok';
+is $comment->created_local, undef, 'inflating null confirmed ok';
diff --git a/t/app/model/db.t b/t/app/model/db.t
new file mode 100644
index 000000000..bebd68f0b
--- /dev/null
+++ b/t/app/model/db.t
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use_ok 'FixMyStreet::App::Model::DB';
+
+done_testing();
diff --git a/t/app/model/problem.t b/t/app/model/problem.t
new file mode 100644
index 000000000..1b8804fce
--- /dev/null
+++ b/t/app/model/problem.t
@@ -0,0 +1,155 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet;
+use FixMyStreet::App;
+use mySociety::Locale;
+
+mySociety::Locale::gettext_domain('FixMyStreet');
+
+my $problem_rs = FixMyStreet::App->model('DB::Problem');
+
+my $problem = $problem_rs->new(
+ {
+ postcode => 'EH99 1SP',
+ latitude => 1,
+ longitude => 1,
+ areas => 1,
+ title => '',
+ detail => '',
+ used_map => 1,
+ user_id => 1,
+ name => '',
+ state => 'confirmed',
+ service => '',
+ cobrand => 'default',
+ cobrand_data => '',
+ }
+);
+
+is $problem->confirmed_local, undef, 'inflating null confirmed ok';
+is $problem->whensent_local, undef, 'inflating null confirmed ok';
+is $problem->lastupdate_local, undef, 'inflating null confirmed ok';
+is $problem->created_local, undef, 'inflating null confirmed ok';
+
+for my $test (
+ {
+ desc => 'more or less empty problem',
+ changed => {},
+ errors => {
+ title => 'Please enter a subject',
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ name => 'Please enter your name',
+ }
+ },
+ {
+ desc => 'name too short',
+ changed => {
+ name => 'xx',
+ },
+ errors => {
+ title => 'Please enter a subject',
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ name => 'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box',
+ }
+ },
+ {
+ desc => 'name is anonymous',
+ changed => {
+ name => 'anonymous',
+ },
+ errors => {
+ title => 'Please enter a subject',
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ name => 'Please enter your full name, councils need this information - if you do not wish your name to be shown on the site, untick the box',
+ }
+ },
+ {
+ desc => 'correct name',
+ changed => {
+ name => 'A User',
+ },
+ errors => {
+ title => 'Please enter a subject',
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ }
+ },
+ {
+ desc => 'correct title',
+ changed => {
+ title => 'A Title',
+ },
+ errors => {
+ detail => 'Please enter some details',
+ council => 'No council selected',
+ }
+ },
+ {
+ desc => 'correct detail',
+ changed => {
+ detail => 'Some information about the problem',
+ },
+ errors => {
+ council => 'No council selected',
+ }
+ },
+ {
+ desc => 'incorrectly formatted council',
+ changed => {
+ council => 'my council',
+ },
+ errors => {
+ council => 'No council selected',
+ }
+ },
+ {
+ desc => 'correctly formatted council',
+ changed => {
+ council => '1001',
+ },
+ errors => {
+ }
+ },
+ {
+ desc => 'bad category',
+ changed => {
+ category => '-- Pick a category --',
+ },
+ errors => {
+ category => 'Please choose a category',
+ }
+ },
+ {
+ desc => 'bad category',
+ changed => {
+ category => '-- Pick a property type --',
+ },
+ errors => {
+ category => 'Please choose a property type',
+ }
+ },
+ {
+ desc => 'correct category',
+ changed => {
+ category => 'Horse!',
+ },
+ errors => {
+ }
+ },
+) {
+ $problem->$_( $test->{changed}->{$_} ) for keys %{$test->{changed}};
+
+ subtest $test->{desc} => sub {
+ is_deeply $problem->check_for_errors, $test->{errors}, 'check for errors';
+ };
+}
+
+done_testing();
diff --git a/t/app/model/token.t b/t/app/model/token.t
new file mode 100644
index 000000000..12945975e
--- /dev/null
+++ b/t/app/model/token.t
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 45;
+
+use FixMyStreet;
+use FixMyStreet::App;
+use mySociety::AuthToken;
+use mySociety::DBHandle 'dbh';
+
+# set things up so that code using mySociety::DBHandle is happy
+FixMyStreet->configure_mysociety_dbhandle();
+
+# NOTE - remember that you need to explicitly dbh()->commit after making
+# database changes with the mySociety::* modules.
+
+# create a token using DBIC and check we can read it using AuthToken, and vice
+# versa
+
+my %tests = (
+ nested_hash => { foo => 'bar', and => [ 'baz', 'bundy' ] },
+ array => [ 'foo', 'bar' ],
+ scalar => 123,
+);
+
+my $token_rs = FixMyStreet::App->model('DB::Token');
+
+# create using DBIC
+foreach my $test_data_name ( sort keys %tests ) {
+ my $test_data = $tests{$test_data_name};
+
+ pass "--- testing DBIC create using '$test_data_name'";
+
+ my $dbic_token =
+ $token_rs->create( { scope => 'testing', data => $test_data } );
+ my $token = $dbic_token->token;
+ ok $token, "stored token '$token'";
+
+ is_deeply $dbic_token->data, $test_data, "data stored correctly using DBIC";
+
+ # read back using DBIC
+ is_deeply $token_rs->find( { token => $token, scope => 'testing' } )->data,
+ $test_data,
+ "data read back correctly with DBIC";
+
+ # read back using mySociety::AuthToken
+ is_deeply mySociety::AuthToken::retrieve( 'testing', $token ),
+ $test_data, "data read back correctly with m::AT";
+
+ # delete token
+ ok $dbic_token->delete, "delete token";
+
+ is $token_rs->find( { token => $token, scope => 'testing' } ),
+ undef,
+ "token gone for DBIC";
+
+ # read back using mySociety::AuthToken
+ is mySociety::AuthToken::retrieve( 'testing', $token ),
+ undef, "token gone with m::AT";
+
+}
+
+# create using m::AT
+foreach my $test_data_name ( sort keys %tests ) {
+ my $test_data = $tests{$test_data_name};
+
+ pass "--- testing m::AT create using '$test_data_name'";
+
+ my $token = mySociety::AuthToken::store( 'testing', $test_data );
+ dbh->commit();
+ ok $token, "stored token '$token'";
+
+ # read back using DBIC
+ is_deeply $token_rs->find( { token => $token, scope => 'testing' } )->data,
+ $test_data,
+ "data read back correctly with DBIC";
+
+ # read back using mySociety::AuthToken
+ is_deeply mySociety::AuthToken::retrieve( 'testing', $token ),
+ $test_data, "data read back correctly with m::AT";
+
+ # delete token
+ ok mySociety::AuthToken::destroy( 'testing', $token ), "destroy token";
+ dbh->commit();
+
+ is $token_rs->find( { token => $token, scope => 'testing' } ),
+ undef,
+ "token gone for DBIC";
+
+ # read back using mySociety::AuthToken
+ is mySociety::AuthToken::retrieve( 'testing', $token ),
+ undef, "token gone with m::AT";
+
+}
diff --git a/t/app/uri_for.t b/t/app/uri_for.t
new file mode 100644
index 000000000..51a6e8a0e
--- /dev/null
+++ b/t/app/uri_for.t
@@ -0,0 +1,108 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+# FIXME Should this be here? A better way? uri_for varies by map.
+use Test::WWW::Mechanize::Catalyst 'FixMyStreet::App';
+FixMyStreet::Map::set_map_class();
+
+# structure of these tests borrowed from '/t/aggregate/unit_core_uri_for.t'
+
+use strict;
+use warnings;
+use URI;
+
+use_ok('FixMyStreet::App');
+
+my $fms_c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://www.fixmystreet.com/'),
+ uri => URI->new('http://www.fixmystreet.com/test_namespace')
+ }
+ ),
+ namespace => 'test_namespace',
+ }
+);
+
+my $fgm_c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://www.fiksgatami.no/'),
+ uri => URI->new('http://www.fiksgatami.no/test_namespace')
+ }
+ ),
+ namespace => 'test_namespace',
+ }
+);
+
+my $reh_en_c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://reportemptyhomes.com/'),
+ uri => URI->new('http://reportemptyhomes.com/test_namespace')
+ }
+ ),
+ namespace => 'test_namespace',
+ }
+);
+$reh_en_c->setup_request();
+
+
+is(
+ $fms_c->uri_for('/bar/baz') . "",
+ 'http://www.fixmystreet.com/bar/baz',
+ 'URI for absolute path'
+);
+
+is(
+ $fms_c->uri_for('') . "",
+ 'http://www.fixmystreet.com/test_namespace',
+ 'URI for namespace'
+);
+
+is(
+ $fms_c->uri_for( '/bar/baz', 'boing', { foo => 'bar', } ) . "",
+ 'http://www.fixmystreet.com/bar/baz/boing?foo=bar',
+ 'URI with query'
+);
+
+# fiksgatami
+is(
+ $fgm_c->uri_for( '/foo', { lat => 1.23, } ) . "",
+ 'http://www.fiksgatami.no/foo?lat=1.23&zoom=3',
+ 'FiksGataMi url with lat not zoom'
+);
+
+like(
+ $reh_en_c->uri_for_email( '/foo' ),
+ qr{^http://en.},
+ 'adds en to retain language'
+);
+
+# instantiate this here otherwise sets locale to cy and breaks test
+# above
+my $reh_cy_c = FixMyStreet::App->new(
+ {
+ request => Catalyst::Request->new(
+ {
+ base => URI->new('http://cy.reportemptyhomes.com/'),
+ uri => URI->new('http://cy.reportemptyhomes.com/test_namespace')
+ }
+ ),
+ namespace => 'test_namespace',
+ }
+);
+$reh_cy_c->setup_request();
+
+like(
+ $reh_cy_c->uri_for_email( '/foo' ),
+ qr{^http://cy.},
+ 'retains language'
+);
+
+done_testing();
diff --git a/t/app/view/email.t b/t/app/view/email.t
new file mode 100644
index 000000000..4d7bbe8ff
--- /dev/null
+++ b/t/app/view/email.t
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+BEGIN { use_ok 'FixMyStreet::App::View::Email' }
+
+done_testing();
diff --git a/t/app/view/web.t b/t/app/view/web.t
new file mode 100644
index 000000000..0f49b986b
--- /dev/null
+++ b/t/app/view/web.t
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+use Test::More;
+
+BEGIN { use_ok 'FixMyStreet::App::View::Web' }
+
+done_testing();
diff --git a/t/cobrand/loading.t b/t/cobrand/loading.t
new file mode 100644
index 000000000..405ef4761
--- /dev/null
+++ b/t/cobrand/loading.t
@@ -0,0 +1,71 @@
+use strict;
+use warnings;
+
+use Test::More;
+use Sub::Override;
+
+use FixMyStreet;
+
+use_ok 'FixMyStreet::Cobrand';
+
+# check that the allowed cobrands is correctly loaded from config
+{
+ my $allowed = FixMyStreet::Cobrand->get_allowed_cobrands;
+ ok $allowed, "got the allowed_cobrands";
+ isa_ok $allowed, "ARRAY";
+ cmp_ok scalar @$allowed, '>', 1, "got more than one";
+ is join( '|', @$allowed ), FixMyStreet->config('ALLOWED_COBRANDS'),
+ "matches config value";
+}
+
+# fake the allowed cobrands for testing
+my $override = Sub::Override->new( #
+ 'FixMyStreet::Cobrand::get_allowed_cobrands' =>
+ sub { return ['emptyhomes'] }
+);
+is_deeply FixMyStreet::Cobrand->get_allowed_cobrands, ['emptyhomes'],
+ 'overidden get_allowed_cobrands';
+
+sub run_host_tests {
+ my %host_tests = @_;
+ for my $host ( sort keys %host_tests ) {
+ is FixMyStreet::Cobrand->get_class_for_host($host),
+ "FixMyStreet::Cobrand::$host_tests{$host}",
+ "does $host -> F::C::$host_tests{$host}";
+ }
+}
+
+# get the cobrand class by host
+run_host_tests(
+ 'www.fixmystreet.com' => 'Default',
+ 'reportemptyhomes.com' => 'EmptyHomes',
+ 'barnet.fixmystreet.com' => 'Default', # not in the allowed_cobrands list
+ 'some.odd.site.com' => 'Default',
+);
+
+# now enable barnet too and check that it works
+$override->replace( #
+ 'FixMyStreet::Cobrand::get_allowed_cobrands' =>
+ sub { return [ 'emptyhomes', 'barnet' ] }
+);
+
+# get the cobrand class by host
+run_host_tests(
+ 'www.fixmystreet.com' => 'Default',
+ 'reportemptyhomes.com' => 'EmptyHomes',
+ 'barnet.fixmystreet.com' => 'Barnet', # found now it is in allowed_cobrands
+ 'some.odd.site.com' => 'Default',
+);
+
+# check that the moniker works as expected both on class and object.
+is FixMyStreet::Cobrand::EmptyHomes->moniker, 'emptyhomes',
+ 'class->moniker works';
+is FixMyStreet::Cobrand::EmptyHomes->new->moniker, 'emptyhomes',
+ 'object->moniker works';
+
+# check is_default works
+ok FixMyStreet::Cobrand::Default->is_default, '::Default is default';
+ok !FixMyStreet::Cobrand::EmptyHomes->is_default, '::Emptyhomes is not default';
+
+# all done
+done_testing();
diff --git a/t/fixmystreet.t b/t/fixmystreet.t
new file mode 100644
index 000000000..d7f00b047
--- /dev/null
+++ b/t/fixmystreet.t
@@ -0,0 +1,37 @@
+use strict;
+use warnings;
+use Path::Class;
+
+use Test::More;
+use Test::Exception;
+
+use_ok 'FixMyStreet';
+
+# check that the path_to works
+my $file_path = file(__FILE__)->absolute->stringify;
+my $path_to_path = FixMyStreet->path_to('t/fixmystreet.t');
+
+isa_ok $path_to_path, 'Path::Class::File';
+ok $path_to_path->is_absolute, "path is absolute";
+is "$path_to_path", $file_path, "got $file_path";
+
+# check that the config gets loaded and is immutable
+my $config = FixMyStreet->config;
+isa_ok $config, 'HASH';
+is $config->{GAZE_URL}, 'http://gaze.mysociety.org/gaze',
+ "got GAZE_URL correctly";
+throws_ok(
+ sub { $config->{GAZE_URL} = 'some other value'; },
+ qr/Modification of a read-only value attempted/,
+ 'attempt to change config caught'
+);
+is $config->{GAZE_URL}, 'http://gaze.mysociety.org/gaze', "GAZE_URL unchanged";
+
+# check that we can get the value by key as well
+is FixMyStreet->config('GAZE_URL'), 'http://gaze.mysociety.org/gaze',
+ "GAZE_URL correct when got by key";
+is FixMyStreet->config('BAD_KEY_DOES_NOT_EXIST'), undef, "config miss is undef";
+
+# all done
+done_testing();
+
diff --git a/t/i18n.t b/t/i18n.t
new file mode 100644
index 000000000..2279ca6a8
--- /dev/null
+++ b/t/i18n.t
@@ -0,0 +1,121 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+use FixMyStreet;
+use mySociety::Locale;
+use Encode;
+use Data::Dumper;
+use Sort::Key qw(keysort);
+use POSIX 'strcoll';
+local $Data::Dumper::Sortkeys = 1;
+use utf8;
+
+# check that the mo files have been generated
+die "You need to run 'commonlib/bin/gettext-makemo --quiet FixMyStreet' "
+ . "to generate the *.mo files needed."
+ unless -e FixMyStreet->path_to(
+ 'locale/cy_GB.UTF-8/LC_MESSAGES/FixMyStreet-EmptyHomes.mo');
+
+# Example strings
+my $english = "Sorry! Something's gone wrong.";
+my $welsh = "Ymddiheuriadau! Mae rhywbeth wedi mynd o'i le.";
+
+# set english as the language
+mySociety::Locale::negotiate_language( #
+ 'en-gb,English,en_GB|cy,Cymraeg,cy_GB', 'en_GB'
+);
+
+mySociety::Locale::gettext_domain( 'FixMyStreet-EmptyHomes', 1 );
+mySociety::Locale::change();
+is _($english), $english, "english to english";
+
+# set to welsh and check for translation
+mySociety::Locale::change('cy');
+is _($english), $welsh, "english to welsh";
+
+# check that being in a deep directory does not confuse the code
+chdir FixMyStreet->path_to('t/app/controller') . '';
+mySociety::Locale::gettext_domain( 'FixMyStreet-EmptyHomes', 1,
+ FixMyStreet->path_to('locale')->stringify );
+mySociety::Locale::change('cy');
+is _($english), $welsh, "english to welsh (deep directory)";
+
+# test that sorting works as expected in the right circumstances...
+my @random_sorted = qw( Å Z Ø A );
+my @EN_sorted = qw( A Å Ø Z );
+my @NO_sorted = qw( A Z Ø Å );
+my @default_sorted = qw( A Z Å Ø );
+
+sub utf8_diag {
+ diag encode_utf8( Dumper(@_) );
+}
+
+{
+
+ mySociety::Locale::negotiate_language( #
+ 'en-gb,English,en_GB|cy,Cymraeg,cy_GB', 'en_GB'
+ );
+ mySociety::Locale::change();
+
+ no locale;
+
+ is_deeply( [ sort @random_sorted ],
+ \@default_sorted, "sort correctly with no locale" );
+
+ is_deeply( [ keysort { $_ } @random_sorted ],
+ \@default_sorted, "keysort correctly with no locale" );
+
+ # Note - this obeys the locale
+ is_deeply( [ sort { strcoll( $a, $b ) } @random_sorted ],
+ \@EN_sorted, "sort strcoll correctly with no locale (to 'en_GB')" );
+}
+
+{
+ mySociety::Locale::negotiate_language( #
+ 'en-gb,English,en_GB|cy,Cymraeg,cy_GB', 'en_GB'
+ );
+ mySociety::Locale::change();
+ use locale;
+
+ is_deeply( [ sort @random_sorted ],
+ \@EN_sorted, "sort correctly with use locale 'en_GB'" );
+
+ # is_deeply( [ keysort { $_ } @random_sorted ],
+ # \@EN_sorted, "keysort correctly with use locale 'en_GB'" );
+
+ is_deeply( [ sort { strcoll( $a, $b ) } @random_sorted ],
+ \@EN_sorted, "sort strcoll correctly with use locale 'en_GB'" );
+}
+
+{
+ mySociety::Locale::negotiate_language( #
+ 'nb-no,Norwegian,nb_NO', 'nb_NO'
+ );
+ mySociety::Locale::change();
+ use locale;
+
+ is_deeply( [ sort @random_sorted ],
+ \@NO_sorted, "sort correctly with use locale 'nb_NO'" );
+
+ # is_deeply( [ keysort { $_ } @random_sorted ],
+ # \@NO_sorted, "keysort correctly with use locale 'nb_NO'" );
+
+ is_deeply( [ sort { strcoll( $a, $b ) } @random_sorted ],
+ \@NO_sorted, "sort strcoll correctly with use locale 'nb_NO'" );
+}
+
+subtest "check that code is only called once by in_gb_locale" => sub {
+
+ my $scalar_counter = 0;
+ my $out = mySociety::Locale::in_gb_locale { $scalar_counter++ };
+ is $scalar_counter, 1, "code called once in scalar context";
+
+ my $list_counter = 0;
+ my @out = mySociety::Locale::in_gb_locale { $list_counter++ };
+ is $list_counter, 1, "code called once in list context";
+
+};
+
+done_testing();
diff --git a/t/templates/mysite/test-header b/t/templates/mysite/test-header
deleted file mode 100644
index 83d959d3d..000000000
--- a/t/templates/mysite/test-header
+++ /dev/null
@@ -1 +0,0 @@
-My test header template