From 9057ce0ce3998b5f2ed54748d32f521052e887fe Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Tue, 12 Aug 2025 16:52:34 +0200 Subject: [PATCH] WebHost: fix links on sitemap, switch to url_for and add test to prevent future breakage (#5318) --- WebHostLib/templates/siteMap.html | 44 ++++++++++----------- test/general/__init__.py | 9 ++++- test/webhost/test_sitemap.py | 63 +++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 test/webhost/test_sitemap.py diff --git a/WebHostLib/templates/siteMap.html b/WebHostLib/templates/siteMap.html index b7db8227..4b764c34 100644 --- a/WebHostLib/templates/siteMap.html +++ b/WebHostLib/templates/siteMap.html @@ -11,32 +11,32 @@

Site Map

Base Pages

Tutorials

Game Info Pages

diff --git a/test/general/__init__.py b/test/general/__init__.py index 34df741a..92ffc77e 100644 --- a/test/general/__init__.py +++ b/test/general/__init__.py @@ -3,7 +3,7 @@ from typing import List, Optional, Tuple, Type, Union from BaseClasses import CollectionState, Item, ItemClassification, Location, MultiWorld, Region from worlds import network_data_package -from worlds.AutoWorld import World, call_all +from worlds.AutoWorld import World, WebWorld, call_all gen_steps = ( "generate_early", @@ -17,7 +17,7 @@ gen_steps = ( def setup_solo_multiworld( - world_type: Type[World], steps: Tuple[str, ...] = gen_steps, seed: Optional[int] = None + world_type: Type[World], steps: Tuple[str, ...] = gen_steps, seed: Optional[int] = None ) -> MultiWorld: """ Creates a multiworld with a single player of `world_type`, sets default options, and calls provided gen steps. @@ -62,11 +62,16 @@ def setup_multiworld(worlds: Union[List[Type[World]], Type[World]], steps: Tuple return multiworld +class TestWebWorld(WebWorld): + tutorials = [] + + class TestWorld(World): game = f"Test Game" item_name_to_id = {} location_name_to_id = {} hidden = True + web = TestWebWorld() # add our test world to the data package, so we can test it later diff --git a/test/webhost/test_sitemap.py b/test/webhost/test_sitemap.py new file mode 100644 index 00000000..930aa324 --- /dev/null +++ b/test/webhost/test_sitemap.py @@ -0,0 +1,63 @@ +import urllib.parse +import html +import re +from flask import url_for + +import WebHost +from . import TestBase + + +class TestSitemap(TestBase): + + # Codes for OK and some redirects that we use + valid_status_codes = [200, 302, 308] + + @classmethod + def setUpClass(cls) -> None: + super().setUpClass() + WebHost.copy_tutorials_files_to_static() + + def test_sitemap_route(self) -> None: + """Verify that the sitemap route works correctly and renders the template without errors.""" + with self.app.test_request_context(): + # Test the /sitemap route + with self.client.open("/sitemap") as response: + self.assertEqual(response.status_code, 200) + self.assertIn(b"Site Map", response.data) + + # Test the /index route which should also serve the sitemap + with self.client.open("/index") as response: + self.assertEqual(response.status_code, 200) + self.assertIn(b"Site Map", response.data) + + # Test using url_for with the function name + with self.client.open(url_for('get_sitemap')) as response: + self.assertEqual(response.status_code, 200) + self.assertIn(b'Site Map', response.data) + + def test_sitemap_links(self) -> None: + """ + Verify that all links in the sitemap are valid by making a request to each one. + """ + with self.app.test_request_context(): + with self.client.open(url_for("get_sitemap")) as response: + self.assertEqual(response.status_code, 200) + html_content = response.data.decode() + + # Extract all href links using regex + href_pattern = re.compile(r'href=["\'](.*?)["\']') + links = href_pattern.findall(html_content) + + self.assertTrue(len(links) > 0, "No links found in sitemap") + + # Test each link + for link in links: + # Skip external links + if link.startswith(("http://", "https://")): + continue + + link = urllib.parse.unquote(html.unescape(link)) + + with self.client.open(link) as response, self.subTest(link=link): + self.assertIn(response.status_code, self.valid_status_codes, + f"Link {link} returned invalid status code {response.status_code}")