Improve stop search and add documentation
This commit is contained in:
parent
42484907ac
commit
71e1b57164
9 changed files with 358 additions and 37 deletions
|
|
@ -25,9 +25,13 @@ def test_geocode_limits_search_to_great_britain_and_identifies_app() -> None:
|
|||
json=[{"lat": "51.45", "lon": "-2.59", "display_name": "Bristol, England"}],
|
||||
)
|
||||
result = core.geocode("Bristol")
|
||||
assert result == {"lat": 51.45, "lon": -2.59, "label": "Bristol, England"}
|
||||
assert result == [{
|
||||
"lat": 51.45, "lon": -2.59, "label": "Bristol, England", "type": None,
|
||||
}]
|
||||
request = responses.calls[0].request
|
||||
assert "countrycodes=gb" in request.url
|
||||
assert "limit=20" in request.url
|
||||
assert "bounded=0" in request.url
|
||||
assert request.headers["User-Agent"].startswith("uk-bus-stops/")
|
||||
assert "edward@4angle.com" in request.headers["User-Agent"]
|
||||
|
||||
|
|
@ -143,6 +147,7 @@ def test_place_name_is_not_mistaken_for_atco_code() -> None:
|
|||
"""Long alphabetic place names still go through Nominatim geocoding."""
|
||||
assert ATCO_PATTERN.fullmatch("Manchester") is None
|
||||
assert ATCO_PATTERN.fullmatch("0100BRP90314") is not None
|
||||
assert ATCO_PATTERN.fullmatch("010000056") is not None
|
||||
|
||||
|
||||
def test_parse_coordinates_accepts_valid_pair_and_rejects_invalid_pair() -> None:
|
||||
|
|
@ -165,6 +170,45 @@ def test_search_coordinates_bypasses_nominatim(client: Any) -> None:
|
|||
assert responses.calls[0].request.url.startswith(core.OVERPASS_URL)
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_geocode_uses_unbounded_map_bias() -> None:
|
||||
"""The map view biases Nominatim ranking without restricting its results."""
|
||||
responses.get(core.NOMINATIM_URL, json=[])
|
||||
assert core.geocode("North Street", viewbox=(-2.7, 51.4, -2.5, 51.5)) == []
|
||||
url = responses.calls[0].request.url
|
||||
assert "viewbox=-2.7%2C51.5%2C-2.5%2C51.4" in url
|
||||
assert "bounded=0" in url
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_place_search_returns_uk_choices_before_loading_stops(client: Any) -> None:
|
||||
"""Ambiguous place searches return UK Nominatim choices without querying Overpass."""
|
||||
responses.get(core.NOMINATIM_URL, json=[
|
||||
{"lat": "51.1", "lon": "-1.3", "display_name": "North Street, Winchester",
|
||||
"type": "residential"},
|
||||
{"lat": "51.4", "lon": "-2.6", "display_name": "North Street, Bristol",
|
||||
"type": "secondary"},
|
||||
])
|
||||
response = client.get(
|
||||
"/api/search?q=North+Street&west=-2.7&south=51.4&east=-2.5&north=51.5"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert data["kind"] == "geocode"
|
||||
assert [item["label"] for item in data["locations"]] == [
|
||||
"North Street, Winchester", "North Street, Bristol",
|
||||
]
|
||||
assert len(responses.calls) == 1
|
||||
assert "viewbox=-2.7%2C51.5%2C-2.5%2C51.4" in responses.calls[0].request.url
|
||||
|
||||
|
||||
def test_search_rejects_partial_viewbox(client: Any) -> None:
|
||||
"""Search-bias bounds must contain all four valid coordinates."""
|
||||
response = client.get("/api/search?q=North+Street&west=-2.7")
|
||||
assert response.status_code == 400
|
||||
assert response.get_json()["error"] == "invalid_viewbox"
|
||||
|
||||
|
||||
def test_location_coordinates_are_validated(client: Any) -> None:
|
||||
"""Latitude and longitude must be numeric and within geographic ranges."""
|
||||
assert client.get("/api/stops?lat=hello&lon=1").status_code == 400
|
||||
|
|
@ -211,3 +255,16 @@ def test_index_contains_search_and_map(client: Any) -> None:
|
|||
assert b"Find a UK bus stop code" in response.data
|
||||
assert b'id="search-input"' in response.data
|
||||
assert b'id="map"' in response.data
|
||||
assert b'href="/about"' in response.data
|
||||
|
||||
|
||||
def test_about_documents_urls_and_services(client: Any) -> None:
|
||||
"""The About page documents shareable links, APIs, and OSM data sources."""
|
||||
response = client.get("/about")
|
||||
assert response.status_code == 200
|
||||
assert b"Shareable URL parameters" in response.data
|
||||
assert b"?q=Bristol+Temple+Meads" in response.data
|
||||
assert b"?node=485403178" in response.data
|
||||
assert b"?lat=51.4545&lon=-2.5879" in response.data
|
||||
assert b"overpass.atownsend.org.uk" in response.data
|
||||
assert b"JSON endpoints" in response.data
|
||||
|
|
|
|||
|
|
@ -39,7 +39,11 @@ def test_search_and_stop_details_in_browser(chromium_browser: Any, flask_url: st
|
|||
"""A location search renders stops, an ATCO code, tags, and route metadata."""
|
||||
page = chromium_browser.new_page(viewport={"width": 1280, "height": 800})
|
||||
page_errors: list[str] = []
|
||||
search_requests: list[str] = []
|
||||
page.on("pageerror", lambda error: page_errors.append(str(error)))
|
||||
page.on("request", lambda request: (
|
||||
search_requests.append(request.url) if "/api/search?" in request.url else None
|
||||
))
|
||||
stop = {
|
||||
"type": "node", "id": 123, "lat": 51.451, "lon": -2.591,
|
||||
"name": "Central Stop", "atco_code": "0100BRP90314", "indicator": "N",
|
||||
|
|
@ -48,14 +52,12 @@ def test_search_and_stop_details_in_browser(chromium_browser: Any, flask_url: st
|
|||
"tags": {"highway": "bus_stop", "shelter": "yes"},
|
||||
}
|
||||
page.route("**/api/search?*", lambda route: route.fulfill(json={
|
||||
"kind": "location",
|
||||
"location": {"lat": 51.45, "lon": -2.59, "label": "Bristol"},
|
||||
"stops": [{
|
||||
"type": "node", "id": 125, "lat": 51.47, "lon": -2.61,
|
||||
"name": "Far Stop", "atco_code": "0100BRP90316", "indicator": None,
|
||||
"bearing": "E", "transport_type": "Bus stop",
|
||||
"tags": {"highway": "bus_stop"},
|
||||
}, stop],
|
||||
"kind": "geocode",
|
||||
"query": "Bristol",
|
||||
"locations": [
|
||||
{"lat": 51.45, "lon": -2.59, "label": "Bristol Centre", "type": "city"},
|
||||
{"lat": 51.2, "lon": -2.7, "label": "Bristol Road, Somerset", "type": "road"},
|
||||
],
|
||||
}))
|
||||
page.route("**/api/stops?*", lambda route: route.fulfill(json={
|
||||
"kind": "location",
|
||||
|
|
@ -85,8 +87,12 @@ def test_search_and_stop_details_in_browser(chromium_browser: Any, flask_url: st
|
|||
page.get_by_label("Location or ATCO code").fill("Bristol")
|
||||
page.get_by_role("button", name="Search").click()
|
||||
playwright_api.expect(page).to_have_url(re.compile(r"\?q=Bristol$"))
|
||||
assert all(key in search_requests[-1] for key in ("west=", "south=", "east=", "north="))
|
||||
playwright_api.expect(page.get_by_text("2 UK matches for “Bristol”", exact=True)).to_be_visible()
|
||||
page.get_by_text("Bristol Centre", exact=True).click()
|
||||
playwright_api.expect(page).to_have_url(re.compile(r"\?lat=51.45&lon=-2.59$"))
|
||||
playwright_api.expect(page.get_by_text("Central Stop", exact=True)).to_be_visible()
|
||||
assert page.locator("#stop-list .stop-title").all_inner_texts() == ["Central Stop", "Far Stop"]
|
||||
assert page.locator("#stop-list .stop-title").all_inner_texts() == ["Central Stop", "Nearby Stop"]
|
||||
playwright_api.expect(page.locator("#stop-list .stop-meta").first).to_contain_text("m · Bus stop")
|
||||
playwright_api.expect(page.locator("#stop-list .stop-meta").first).to_contain_text("NW-bound")
|
||||
assert page.evaluate("map.getZoom()") > 6
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue