import asyncio import pytest from scrapling import Selector class TestParserAdaptive: def test_element_relocation(self): """Test relocating element after structure change""" original_html = """

Product 1

Description 1

Product 2

Description 2

""" changed_html = """

Product 1

Description 1

Product 2

Description 2

""" old_page = Selector(original_html, url="example.com", adaptive=True) new_page = Selector(changed_html, url="example.com", adaptive=True) # 'p1' was used as ID and now it's not and all the path elements have changes # Also at the same time testing `adaptive` vs combined selectors _ = old_page.css("#p1, #p2", auto_save=True)[0] relocated = new_page.css("#p1", adaptive=True) assert relocated is not None assert relocated[0].attrib["data-id"] == "p1" assert relocated[0].has_class("new-class") assert relocated[0].css(".new-description")[0].text == "Description 1" def test_relocation_auto_save_no_match_above_threshold(self): """Adaptive relocation with `auto_save=True` must not crash when no element clears the `percentage` threshold (relocate() returns an empty list).""" original_html = """

Widget

A widget

""" # Unrelated structure so nothing can match a high threshold changed_html = "totally unrelated content" old_page = Selector(original_html, url="example.com", adaptive=True) new_page = Selector(changed_html, url="example.com", adaptive=True) old_page.css("#target", identifier="target", auto_save=True) # Before the fix this raised `IndexError: list index out of range` because the # guard checked `elements is not None` but relocate() returns [] (never None). result = new_page.css( "#target", identifier="target", adaptive=True, auto_save=True, percentage=95 ) assert list(result) == [] @pytest.mark.asyncio async def test_element_relocation_async(self): """Test relocating element after structure change in async mode""" original_html = """

Product 1

Description 1

Product 2

Description 2

""" changed_html = """

Product 1

Description 1

Product 2

Description 2

""" # Simulate async operation await asyncio.sleep(0.1) # Minimal async operation old_page = Selector(original_html, url="example.com", adaptive=True) new_page = Selector(changed_html, url="example.com", adaptive=True) # 'p1' was used as ID and now it's not and all the path elements have changes # Also at the same time testing `adaptive` vs combined selectors _ = old_page.css("#p1, #p2", auto_save=True)[0] relocated = new_page.css("#p1", adaptive=True) assert relocated is not None assert relocated[0].attrib["data-id"] == "p1" assert relocated[0].has_class("new-class") assert relocated[0].css(".new-description")[0].text == "Description 1"