diff --git a/src/kleinanzeigen_bot/__init__.py b/src/kleinanzeigen_bot/__init__.py index 534683a..453b3d9 100644 --- a/src/kleinanzeigen_bot/__init__.py +++ b/src/kleinanzeigen_bot/__init__.py @@ -644,28 +644,8 @@ class KleinanzeigenBot(WebScrapingMixin): await self.web_select(By.XPATH, "//select[contains(@id, '.versand_s')]", shipping_value) except TimeoutError: LOG.warning("Failed to set shipping attribute for type '%s'!", ad_cfg['shipping_type']) - elif ad_cfg["shipping_type"] == "PICKUP": - try: - await self.web_click(By.XPATH, - '//*[contains(@class, "ShippingPickupSelector")]//label[text()[contains(.,"Nur Abholung")]]/../input[@type="radio"]') - except TimeoutError as ex: - LOG.debug(ex, exc_info = True) - elif ad_cfg["shipping_options"]: - await self.web_click(By.XPATH, '//*[contains(@class, "ShippingSection")]//*//button[contains(@class, "SelectionButton")]') - await self.web_click(By.CSS_SELECTOR, '[class*="CarrierSelectionModal--Button"]') - await self.__set_shipping_options(ad_cfg) else: - try: - await self.web_click(By.XPATH, - '//*[contains(@class, "ShippingSection")]//*//button[contains(@class, "SelectionButton")]') - await self.web_click(By.CSS_SELECTOR, '[class*="CarrierSelectionModal--Button"]') - await self.web_click(By.CSS_SELECTOR, '[class*="CarrierOption--Main"]') - if ad_cfg["shipping_costs"]: - await self.web_input(By.CSS_SELECTOR, '.IndividualShippingInput input[type="text"]', str.replace(ad_cfg["shipping_costs"], ".", ",") - ) - await self.web_click(By.XPATH, '//*[contains(@class, "ModalDialog--Actions")]//button[.//*[text()[contains(.,"Fertig")]]]') - except TimeoutError as ex: - LOG.debug(ex, exc_info = True) + await self.__set_shipping(ad_cfg) ############################# # set price @@ -904,6 +884,36 @@ class KleinanzeigenBot(WebScrapingMixin): raise TimeoutError(f"Failed to set special attribute [{special_attribute_key}]") from ex LOG.debug("Successfully set attribute field [%s] to [%s]...", special_attribute_key, special_attribute_value) + async def __set_shipping(self, ad_cfg: dict[str, Any]) -> None: + if ad_cfg["shipping_type"] == "PICKUP": + try: + await self.web_click(By.XPATH, + '//*[contains(@class, "ShippingPickupSelector")]//label[text()[contains(.,"Nur Abholung")]]/../input[@type="radio"]') + except TimeoutError as ex: + LOG.debug(ex, exc_info = True) + elif ad_cfg["shipping_options"]: + await self.web_click(By.XPATH, '//*[contains(@class, "ShippingSection")]//*//button[contains(@class, "SelectionButton")]') + await self.web_click(By.CSS_SELECTOR, '[class*="CarrierSelectionModal--Button"]') + await self.__set_shipping_options(ad_cfg) + else: + try: + special_shipping_selector = '//select[contains(@id, ".versand_s")]' + if await self.web_check(By.XPATH, special_shipping_selector, Is.DISPLAYED): + # try to set special attribute selector (then we have a commercial account) + shipping_value = "ja" if ad_cfg["shipping_type"] == "SHIPPING" else "nein" + await self.web_select(By.XPATH, special_shipping_selector, shipping_value) + else: + await self.web_click(By.XPATH, + '//*[contains(@class, "ShippingSection")]//*//button[contains(@class, "SelectionButton")]') + await self.web_click(By.CSS_SELECTOR, '[class*="CarrierSelectionModal--Button"]') + await self.web_click(By.CSS_SELECTOR, '[class*="CarrierOption--Main"]') + if ad_cfg["shipping_costs"]: + await self.web_input(By.CSS_SELECTOR, '.IndividualShippingInput input[type="text"]', str.replace(ad_cfg["shipping_costs"], ".", ",") + ) + await self.web_click(By.XPATH, '//*[contains(@class, "ModalDialog--Actions")]//button[.//*[text()[contains(.,"Fertig")]]]') + except TimeoutError as ex: + LOG.debug(ex, exc_info = True) + async def __set_shipping_options(self, ad_cfg: dict[str, Any]) -> None: shipping_options_mapping = { "DHL_2": ("Klein", "Paket 2 kg"), diff --git a/tests/unit/test_init.py b/tests/unit/test_init.py index af2ab41..1d8461e 100644 --- a/tests/unit/test_init.py +++ b/tests/unit/test_init.py @@ -964,18 +964,25 @@ class TestKleinanzeigenBotShippingOptions: # Create temporary file path ad_file = Path(tmp_path) / "test_ad.yaml" + # Mock web_execute to handle all JavaScript calls + async def mock_web_execute(script: str) -> Any: + if script == 'document.body.scrollHeight': + return 0 # Return integer to prevent scrolling loop + return None + # Mock the necessary web interaction methods - with patch.object(test_bot, 'web_click', new_callable = AsyncMock), \ - patch.object(test_bot, 'web_find', new_callable = AsyncMock) as mock_find, \ + with patch.object(test_bot, 'web_execute', side_effect=mock_web_execute), \ + patch.object(test_bot, 'web_click', new_callable=AsyncMock), \ + patch.object(test_bot, 'web_find', new_callable=AsyncMock) as mock_find, \ patch.object(test_bot, 'web_select', new_callable = AsyncMock), \ patch.object(test_bot, 'web_input', new_callable = AsyncMock), \ patch.object(test_bot, 'web_open', new_callable = AsyncMock), \ patch.object(test_bot, 'web_sleep', new_callable = AsyncMock), \ patch.object(test_bot, 'web_check', new_callable = AsyncMock, return_value = True), \ patch.object(test_bot, 'web_request', new_callable = AsyncMock), \ - patch.object(test_bot, 'web_execute', new_callable = AsyncMock), \ patch.object(test_bot, 'web_find_all', new_callable = AsyncMock) as mock_find_all, \ - patch.object(test_bot, 'web_await', new_callable = AsyncMock): + patch.object(test_bot, 'web_await', new_callable = AsyncMock), \ + patch('builtins.input', return_value=""): # Mock the input function # Mock the shipping options form elements mock_find.side_effect = [