import app as app_module def _client(): app_module.app.config['TESTING'] = True return app_module.app.test_client() def _stub_data(monkeypatch): monkeypatch.setattr(app_module, 'get_cached', lambda key: None) monkeypatch.setattr(app_module, 'set_cached', lambda key, data: None) monkeypatch.setattr( app_module.rtt_scraper, 'fetch', lambda travel_date, user_agent: [ {'depart_bristol': '07:00', 'arrive_paddington': '08:45', 'headcode': '1A23'}, ], ) monkeypatch.setattr( app_module.eurostar_scraper, 'fetch', lambda destination, travel_date, user_agent: [ { 'depart_st_pancras': '10:01', 'arrive_destination': '13:34', 'destination': destination, 'train_number': 'ES 9014', }, ], ) monkeypatch.setattr( app_module.eurostar_scraper, 'timetable_url', lambda destination: f'https://example.test/{destination.lower().replace(" ", "-")}', ) def test_index_shows_fixed_departure_and_destination_radios(): client = _client() resp = client.get('/') html = resp.get_data(as_text=True) assert resp.status_code == 200 assert 'Departure point' in html assert 'Bristol Temple Meads' in html assert html.count('type="radio"') == len(app_module.DESTINATIONS) assert 'destination-rotterdam' in html def test_search_redirects_to_results_with_selected_params(): client = _client() resp = client.get('/search?destination=rotterdam&travel_date=2026-04-10&min_connection=60&max_connection=120') assert resp.status_code == 302 assert resp.headers['Location'].endswith( '/results/rotterdam/2026-04-10?min_connection=60&max_connection=120' ) def test_results_shows_same_day_destination_switcher(monkeypatch): _stub_data(monkeypatch) client = _client() resp = client.get('/results/paris/2026-04-10?min_connection=60&max_connection=120') html = resp.get_data(as_text=True) assert resp.status_code == 200 assert 'Switch destination for Friday 10 April 2026' in html assert 'Paris Gare du Nord' in html assert '/results/brussels/2026-04-10?min_connection=60&max_connection=120' in html assert '/results/rotterdam/2026-04-10?min_connection=60&max_connection=120' in html assert 'ES 9014' in html def test_results_title_and_social_meta_include_destination(monkeypatch): _stub_data(monkeypatch) client = _client() resp = client.get('/results/lille/2026-04-10?min_connection=60&max_connection=120') html = resp.get_data(as_text=True) assert resp.status_code == 200 assert 'Bristol to Lille Europe via Eurostar' in html assert '' in html assert ( '' ) in html assert '' in html def test_results_marks_trips_within_five_minutes_of_fastest_and_slowest(monkeypatch): monkeypatch.setattr(app_module, 'get_cached', lambda key: None) monkeypatch.setattr(app_module, 'set_cached', lambda key, data: None) monkeypatch.setattr( app_module.rtt_scraper, 'fetch', lambda travel_date, user_agent: [ {'depart_bristol': '07:00', 'arrive_paddington': '08:30', 'headcode': '1A01'}, {'depart_bristol': '07:05', 'arrive_paddington': '08:36', 'headcode': '1A02'}, {'depart_bristol': '07:10', 'arrive_paddington': '08:46', 'headcode': '1A03'}, {'depart_bristol': '07:15', 'arrive_paddington': '08:56', 'headcode': '1A04'}, {'depart_bristol': '07:20', 'arrive_paddington': '09:06', 'headcode': '1A05'}, ], ) monkeypatch.setattr( app_module.eurostar_scraper, 'fetch', lambda destination, travel_date, user_agent: [ { 'depart_st_pancras': '09:30', 'arrive_destination': '11:50', 'destination': destination, 'train_number': 'ES 1001', }, { 'depart_st_pancras': '09:40', 'arrive_destination': '12:00', 'destination': destination, 'train_number': 'ES 1002', }, { 'depart_st_pancras': '09:50', 'arrive_destination': '12:20', 'destination': destination, 'train_number': 'ES 1003', }, { 'depart_st_pancras': '10:00', 'arrive_destination': '12:35', 'destination': destination, 'train_number': 'ES 1004', }, { 'depart_st_pancras': '10:10', 'arrive_destination': '12:45', 'destination': destination, 'train_number': 'ES 1005', }, ], ) monkeypatch.setattr( app_module.eurostar_scraper, 'timetable_url', lambda destination: f'https://example.test/{destination.lower().replace(" ", "-")}', ) client = _client() resp = client.get('/results/paris/2026-04-10?min_connection=60&max_connection=120') html = resp.get_data(as_text=True) assert resp.status_code == 200 assert html.count('title="Fastest option"') == 2 assert html.count('title="Slowest option"') == 2 assert '4h 50m ⚡' in html assert '4h 55m ⚡' in html assert '5h 20m 🐢' in html assert '5h 25m 🐢' in html assert '5h 10m ⚡' not in html assert '5h 10m 🐢' not in html def test_results_shows_unreachable_morning_eurostar_services(monkeypatch): monkeypatch.setattr(app_module, 'get_cached', lambda key: None) monkeypatch.setattr(app_module, 'set_cached', lambda key, data: None) monkeypatch.setattr( app_module.rtt_scraper, 'fetch', lambda travel_date, user_agent: [ {'depart_bristol': '07:00', 'arrive_paddington': '08:45', 'headcode': '1A23'}, ], ) monkeypatch.setattr( app_module.eurostar_scraper, 'fetch', lambda destination, travel_date, user_agent: [ { 'depart_st_pancras': '09:30', 'arrive_destination': '12:00', 'destination': destination, 'train_number': 'ES 9001', }, { 'depart_st_pancras': '10:15', 'arrive_destination': '13:40', 'destination': destination, 'train_number': 'ES 9002', }, { 'depart_st_pancras': '12:30', 'arrive_destination': '15:55', 'destination': destination, 'train_number': 'ES 9003', }, ], ) monkeypatch.setattr( app_module.eurostar_scraper, 'timetable_url', lambda destination: f'https://example.test/{destination.lower().replace(" ", "-")}', ) client = _client() resp = client.get('/results/paris/2026-04-10?min_connection=60&max_connection=120') html = resp.get_data(as_text=True) assert resp.status_code == 200 assert '1 morning service unavailable from Bristol' in html assert '09:30' in html assert 'ES 9001' in html assert 'Unavailable from Bristol' in html assert 'Morning means Eurostar departures before 12:00 from St Pancras.' in html assert html.index('09:30') < html.index('10:15') def test_results_can_show_only_unreachable_morning_services(monkeypatch): monkeypatch.setattr(app_module, 'get_cached', lambda key: None) monkeypatch.setattr(app_module, 'set_cached', lambda key, data: None) monkeypatch.setattr( app_module.rtt_scraper, 'fetch', lambda travel_date, user_agent: [ {'depart_bristol': '07:00', 'arrive_paddington': '08:45', 'headcode': '1A23'}, ], ) monkeypatch.setattr( app_module.eurostar_scraper, 'fetch', lambda destination, travel_date, user_agent: [ { 'depart_st_pancras': '09:30', 'arrive_destination': '12:00', 'destination': destination, 'train_number': 'ES 9001', }, ], ) monkeypatch.setattr( app_module.eurostar_scraper, 'timetable_url', lambda destination: f'https://example.test/{destination.lower().replace(" ", "-")}', ) client = _client() resp = client.get('/results/paris/2026-04-10?min_connection=60&max_connection=120') html = resp.get_data(as_text=True) assert resp.status_code == 200 assert 'No valid journeys found.' not in html assert '1 morning service unavailable from Bristol' in html assert '09:30' in html assert 'Unavailable from Bristol' in html