Skip to content

Conversation

@bjspi
Copy link

@bjspi bjspi commented Nov 12, 2025

ℹ️ Description

Added Webselect-Function for Input/Dropdown Combobox
PR for issue/missing feature #677

Fixes / Enhancements

Finding Special Attributes Elements can fail because they are currently only selected using the name="..." attributes of the HTML elements. If it fails, ALSO fallback-handle selecting special attribute HTML elements by ID instead / additionally. (For example the "brands" Input/Combobox for Mens Shoes...

When trying to select a Value in a , it does not only rely on the actual Option value (xxx in the example yyy</...>) but instead also on the displayed HTML value (i.e. yyy in above example). This improves UX because the User doesnt have to check the actual "value" of the Option but instead can check the displayed Value from the Browsers Display directly. Testcases for Webselect_Combobox were not added due to missing knowledge about Async Mocking properly. 📋 Changes Summary ✅ Fixes & Enhancements New WebSelect Functionality Improved Element Detection for Special Attributes Enhanced Option Matching Logic

This improves UX and test robustness — users no longer need to know the exact underlying value, as matching also works with the visible label shown in the browser.

🧩 Result

These updates make dropdown and combobox interactions more intuitive, resilient, and user-friendly across diverse HTML structures.

⚙️ Type of Change

Select the type(s) of change(s) included in this pull request:

  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (adds new functionality without breaking existing usage)
  • 💥 Breaking change (changes that might break existing user setups, scripts, or configurations)

✅ Checklist

Before requesting a review, confirm the following:

  • I have reviewed my changes to ensure they meet the project's standards.
  • I have tested my changes and ensured that all tests pass (pdm run test).
  • I have formatted the code (pdm run format).
  • I have verified that linting passes (pdm run lint).
  • I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Summary by CodeRabbit

  • Bug Fixes

    • Field lookup now falls back to locating by ID when name lookup times out.
    • Option selection uses a two-pass match (value then displayed text); JS-path failures now surface as timeouts.
    • Error and log messages localized and clarified.
  • New Features

    • Support for combobox-style inputs: type into the input, open dropdown, and select by visible text (handles special characters).
  • Tests

    • Added tests for combobox selection, missing dropdowns, no-match errors, value-path selection, and special-character handling.

1) Added Webselect for Input/Dropdown Combobox
PR for issue Second-Hand-Friends#677

Fixes:
2) Finding Special Attributes Elements can fail because they are currently only selected using the name="..." attributes of the HTML elements. If it fails, ALSO fallback-handle selecting special attribute HTML elements by ID instead / additionally. (For example the "brands" Input/Combobox for Mens Shoes...

3) When trying to select a Value in a <select>, it does not only rely on the actual Option value (xxx in the example <options value="xxx">yyy</...>) but instead also on the displayed HTML value (i.e. yyy in above example). This improves UX because the User doesnt have to check the actual "value" of the Option but instead can check the displayed Value from the Browsers Display directly.
@coderabbitai
Copy link

coderabbitai bot commented Nov 12, 2025

Walkthrough

Name-based XPath lookup now falls back to an ID-based XPath before failing. Text inputs with role="combobox" are routed to a new combobox selection path. web_select now tries option.value then visible text (JSON-escaped). Six async tests for combobox and option-selection were added.

Changes

Cohort / File(s) Summary
Element location & attribute handling
src/kleinanzeigen_bot/__init__.py
On XPath name lookup timeout, try an ID-based XPath fallback; if both fail raise a localized TimeoutError mentioning both ID and Name. If a text input has role="combobox", delegate to the new web_select_combobox path. Error messages and logging updated to use translations; minor timeout call formatting adjusted.
Select & combobox utilities
src/kleinanzeigen_bot/utils/web_scraping_mixin.py
web_select: perform a two-path JS match (first compare option.value, then trimmed visible text) using a JSON-escaped target, and convert failures to descriptive TimeoutError. Added web_select_combobox(selector_type, selector_value, selected_value, timeout=None) to type into a combobox input, locate its dropdown via aria-controls, normalize option texts (case-insensitive, whitespace-normalized), click the matched <li> (scrollIntoView) and return the dropdown element; raise descriptive errors when aria-controls is missing or no match found.
Tests (new)
tests/unit/test_web_scraping_mixin.py
Added six async tests covering combobox and option-selection behavior: missing aria-controls (expect TimeoutError and verify input cleared/sent), successful selection with matching option, no matching option (expect TimeoutError), special-character escaping behavior, value-based selection path, and JS-mismatch error case.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Init as __init__.py
    participant Mixin as web_scraping_mixin.py
    participant DOM

    Caller->>Init: set_special_attribute(name, value)
    Init->>Init: XPath lookup by name (with timeout)
    alt found by name
        Init->>Init: inspect element type & role
    else not found by name
        Init->>Init: XPath lookup by ID (fallback)
        alt found by ID
            Init->>Init: inspect element type & role
        else not found by ID
            Init-->>Caller: raise TimeoutError (cannot find by Name or ID)
        end
    end

    alt role == "combobox"
        Init->>Mixin: web_select_combobox(selector, value)
        Mixin->>DOM: locate input, clear input, send keys (typed value)
        Mixin->>DOM: read aria-controls -> find dropdown
        Mixin->>DOM: normalize option texts -> click matching <li> (scrollIntoView)
        Mixin-->>Init: return dropdown element
    else role == "select"
        Init->>Mixin: web_select(selector, value)
        Mixin->>DOM: try match by option.value -> else by trimmed visible text -> click option
        Mixin-->>Init: success / TimeoutError if no match
    else checkbox/text
        Init->>DOM: set value directly
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas needing extra attention:
    • XPath-by-ID fallback correctness and edge cases in src/kleinanzeigen_bot/__init__.py.
    • Timing and race conditions in web_select_combobox (typing -> dropdown population -> aria-controls).
    • JS injection: proper JSON-escaping and normalization for matching in web_select.

Possibly related PRs

Suggested reviewers

  • Heavenfighter

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: combobox support, enhanced element detection, and improved option matching across all three modified files.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c14eb3 and e94e8b0.

⛔ Files ignored due to path filters (1)
  • src/kleinanzeigen_bot/resources/translations.de.yaml is excluded by none and included by none
📒 Files selected for processing (1)
  • src/kleinanzeigen_bot/__init__.py (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/kleinanzeigen_bot/**/*.py

⚙️ CodeRabbit configuration file

src/kleinanzeigen_bot/**/*.py: CRITICAL RULES FOR KLEINANZEIGEN BOT:

  1. ALL code, comments, and text MUST be in English
  2. User-facing messages MUST use translation system (_()) function
  3. NEVER access live website in tests (bot detection risk)
  4. Use WebScrapingMixin for browser automation
  5. Handle TimeoutError for all web operations
  6. Use ensure() for critical validations
  7. Don't add features until explicitly needed
  8. Keep solutions simple and straightforward
  9. Use async/await for I/O operations
  10. Follow Pydantic model patterns
  11. Use proper error handling and logging
  12. Test business logic separately from web scraping
  13. Include SPDX license headers on all Python files
  14. Use type hints for all function parameters and return values
  15. Use structured logging with context

Files:

  • src/kleinanzeigen_bot/__init__.py
🧬 Code graph analysis (1)
src/kleinanzeigen_bot/__init__.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (6)
  • web_find (729-745)
  • By (75-81)
  • web_select (956-1004)
  • web_click (636-646)
  • web_select_combobox (1006-1070)
  • web_input (852-863)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: build (macos-15-intel, 3.14, true)
  • GitHub Check: build (ubuntu-latest, 3.14, true)
  • GitHub Check: build (macos-15-intel, 3.10, false)
  • GitHub Check: build (windows-latest, 3.14, true)
  • GitHub Check: build (macos-latest, 3.10, false)
  • GitHub Check: build (windows-latest, 3.10, false)
  • GitHub Check: build (macos-latest, 3.14, true)
  • GitHub Check: build (ubuntu-latest, 3.10, false)
  • GitHub Check: analyze
🔇 Additional comments (7)
src/kleinanzeigen_bot/__init__.py (7)

1071-1077: ID fallback logic looks good.

The nested exception handling correctly attempts ID-based lookup when name-based fails, and the error message is properly localized. The fallback strategy is sound since some elements use composite names (e.g., "autos.marke_s+autos.model_s") but still have a simple ID attribute.


1087-1089: Combobox detection and routing implemented correctly.

The new branch properly identifies text inputs with role="combobox" and routes them to the web_select_combobox method. The safe .get("role") accessor prevents AttributeError when the role attribute is absent.


1082-1082: Log message properly localized.

Debug message now uses the translation system as required by coding guidelines.


1085-1085: Log message properly localized.

Debug message now uses the translation system as required by coding guidelines.


1091-1091: Log message properly localized.

Debug message now uses the translation system as required by coding guidelines.


1094-1095: Error handling improved with accurate messaging.

The debug message now accurately reflects what was attempted (select, checkbox, combobox, and text input) rather than the previous misleading "radio button" message. Both messages are properly localized and exception chaining is preserved.


1118-1118: Explicit timeout parameter improves readability.

Using the keyword argument form makes the intent clear without requiring readers to check the function signature.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3c278b and 7eb0011.

📒 Files selected for processing (2)
  • src/kleinanzeigen_bot/__init__.py (2 hunks)
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: All Python files must start with SPDX license headers (FileCopyrightText, License-Identifier, ArtifactOfProjectHomePage)
Group imports as: standard library, third-party, then local, with blank lines between groups
Always use type hints for function parameters and return values
Use Any for complex types when necessary
Use Final for constants
Use typing.cast() when the type checker needs help
Use docstrings for complex functions and classes that need explanation; include examples for complex functions
Use comments to explain code logic and reasoning, especially for algorithms, business logic, and non-obvious decisions
Use structured logging via loggers.get_logger(), include context, and choose appropriate log levels
Use specific exception types and include meaningful error messages
Use pyright: ignore[reportAttributeAccessIssue] comments only for known type-checker issues

Files:

  • src/kleinanzeigen_bot/__init__.py
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
src/kleinanzeigen_bot/**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use absolute imports for project modules (e.g., from kleinanzeigen_bot import KleinanzeigenBot)

Files:

  • src/kleinanzeigen_bot/__init__.py
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py

⚙️ CodeRabbit configuration file

src/kleinanzeigen_bot/**/*.py: CRITICAL RULES FOR KLEINANZEIGEN BOT:

  1. ALL code, comments, and text MUST be in English
  2. User-facing messages MUST use translation system (_()) function
  3. NEVER access live website in tests (bot detection risk)
  4. Use WebScrapingMixin for browser automation
  5. Handle TimeoutError for all web operations
  6. Use ensure() for critical validations
  7. Don't add features until explicitly needed
  8. Keep solutions simple and straightforward
  9. Use async/await for I/O operations
  10. Follow Pydantic model patterns
  11. Use proper error handling and logging
  12. Test business logic separately from web scraping
  13. Include SPDX license headers on all Python files
  14. Use type hints for all function parameters and return values
  15. Use structured logging with context

Files:

  • src/kleinanzeigen_bot/__init__.py
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
**/__init__.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add module docstrings for packages (and complex modules) to document purpose and contents

Files:

  • src/kleinanzeigen_bot/__init__.py
src/**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

src/**/*.py: All user-facing output (logs, prints, CLI help) must be written in English
Use the translation system for all output; never hardcode German or other non-English strings in code

Files:

  • src/kleinanzeigen_bot/__init__.py
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
🧬 Code graph analysis (1)
src/kleinanzeigen_bot/__init__.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (3)
  • web_find (655-698)
  • By (71-77)
  • web_select_combobox (884-921)
🪛 Ruff (0.14.4)
src/kleinanzeigen_bot/__init__.py

1068-1068: Avoid specifying long messages outside the exception class

(TRY003)

src/kleinanzeigen_bot/utils/web_scraping_mixin.py

884-884: Dynamically typed expressions (typing.Any) are disallowed in selected_value

(ANN401)


901-901: Avoid specifying long messages outside the exception class

(TRY003)


918-918: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build
  • GitHub Check: build (windows-latest, 3.10, false)
  • GitHub Check: build (macos-latest, 3.14, true)
  • GitHub Check: build (windows-latest, 3.14, true)
  • GitHub Check: build (macos-15-intel, 3.10, false)
  • GitHub Check: build (ubuntu-latest, 3.10, false)
  • GitHub Check: build (macos-15-intel, 3.14, true)
🔇 Additional comments (3)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (1)

851-882: Good security practice with JSON escaping for JavaScript injection.

The updated logic correctly tries matching by value first, then falls back to displayed text. Using json.dumps() for safe JavaScript string escaping is the right approach to prevent injection issues.

src/kleinanzeigen_bot/__init__.py (2)

1062-1068: Enhanced element detection with ID fallback is a good improvement.

The fallback logic correctly attempts to find elements by ID when the XPath name search fails, improving robustness for elements without name attributes (like the "brands" input mentioned in the PR).

However, the static analysis tool flags the long error message at line 1068. Consider extracting this to a custom exception type or a constant for better maintainability, though this is a minor concern.

As per coding guidelines: "Use specific exception types and include meaningful error messages."


1078-1080: Clean integration of combobox handling.

The new branch correctly detects text inputs with role="combobox" and routes them to the appropriate web_select_combobox method. This aligns well with the PR objectives to support input/dropdown combobox interactions.

@1cu 1cu changed the title Improved WebSelect Handling: Added Combobox Support, Enhanced Element Detection, and Smarter Option Matching feat: Improved WebSelect Handling: Added Combobox Support, Enhanced Element Detection, and Smarter Option Matching Nov 12, 2025
@github-actions github-actions bot added the enhancement New feature or request label Nov 12, 2025
Copy link
Contributor

@1cu 1cu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Combobox handling is heading the right way, but I’d block this until a couple of issues are addressed:

  • Translation gate (tests/unit/test_translations.py)web_select_combobox adds two plain-English log strings (“Combobox input field does not have 'aria-controls'…”, “No <li> options found…”). Because they’re not wrapped in _()/
    absent from resources/translations.de.yaml, the translation sweep fails across the entire Build matrix. Wrap the strings in _() and add the German entries (plus optional translations.en.yaml overrides), then rerun pdm run utest.
  • Exception mismatch (src/kleinanzeigen_bot/init.py:1059-1086 & utils/web_scraping_mixin.py:884-921) – the new helper raises AssertionError when the combobox can’t be resolved, but the caller only catches TimeoutError. That
    bubbles up raw assertions without the usual “failed to set special attribute” logging. Either convert the assertion sites to raise TimeoutError(...) (or use ensure(..., exc=TimeoutError)), or broaden the caller’s except so combobox
    failures produce consistent diagnostics.
  • Selection correctness (utils/web_scraping_mixin.py:904-917) – after typing the filter string, the helper blindly clicks the first <li>. Kleinanzeigen comboboxes commonly return multiple suggestions even after full input, so this
    can silently choose the wrong value. Please iterate the <li> nodes (or inspect their data-* attributes) and only click when the visible text equals selected_value; otherwise raise.
  • Missing tests – the PR changes both the <select> JS helper (label fallback) and introduces an entirely new combobox workflow, yet no tests were updated. Add targeted unit/coroutine tests so regressions in the JS selection logic
    and web_select_combobox behavior are caught before hitting CI.

Once these are fixed (translations, exception consistency, true matching, and tests), I’ll gladly take another look.

1) TimeoutError Exception instead of AssertionError
2) Added translations _() and changed translations.de.yaml
3) Improved <li> Selection for Combobox based on actual displayed value

Ran PDM Lint / and PDM Formatting.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7eb0011 and 65c2980.

⛔ Files ignored due to path filters (1)
  • src/kleinanzeigen_bot/resources/translations.de.yaml is excluded by none and included by none
📒 Files selected for processing (2)
  • src/kleinanzeigen_bot/__init__.py (2 hunks)
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: All Python files must start with SPDX license headers (FileCopyrightText, License-Identifier, ArtifactOfProjectHomePage)
Group imports as: standard library, third-party, then local, with blank lines between groups
Always use type hints for function parameters and return values
Use Any for complex types when necessary
Use Final for constants
Use typing.cast() when the type checker needs help
Use docstrings for complex functions and classes that need explanation; include examples for complex functions
Use comments to explain code logic and reasoning, especially for algorithms, business logic, and non-obvious decisions
Use structured logging via loggers.get_logger(), include context, and choose appropriate log levels
Use specific exception types and include meaningful error messages
Use pyright: ignore[reportAttributeAccessIssue] comments only for known type-checker issues

Files:

  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
  • src/kleinanzeigen_bot/__init__.py
src/kleinanzeigen_bot/**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use absolute imports for project modules (e.g., from kleinanzeigen_bot import KleinanzeigenBot)

Files:

  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
  • src/kleinanzeigen_bot/__init__.py

⚙️ CodeRabbit configuration file

src/kleinanzeigen_bot/**/*.py: CRITICAL RULES FOR KLEINANZEIGEN BOT:

  1. ALL code, comments, and text MUST be in English
  2. User-facing messages MUST use translation system (_()) function
  3. NEVER access live website in tests (bot detection risk)
  4. Use WebScrapingMixin for browser automation
  5. Handle TimeoutError for all web operations
  6. Use ensure() for critical validations
  7. Don't add features until explicitly needed
  8. Keep solutions simple and straightforward
  9. Use async/await for I/O operations
  10. Follow Pydantic model patterns
  11. Use proper error handling and logging
  12. Test business logic separately from web scraping
  13. Include SPDX license headers on all Python files
  14. Use type hints for all function parameters and return values
  15. Use structured logging with context

Files:

  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
  • src/kleinanzeigen_bot/__init__.py
src/**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

src/**/*.py: All user-facing output (logs, prints, CLI help) must be written in English
Use the translation system for all output; never hardcode German or other non-English strings in code

Files:

  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
  • src/kleinanzeigen_bot/__init__.py
**/__init__.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add module docstrings for packages (and complex modules) to document purpose and contents

Files:

  • src/kleinanzeigen_bot/__init__.py
🧬 Code graph analysis (1)
src/kleinanzeigen_bot/__init__.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (3)
  • web_find (655-698)
  • By (71-77)
  • web_select_combobox (884-944)
🪛 Ruff (0.14.4)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py

884-884: Dynamically typed expressions (typing.Any) are disallowed in selected_value

(ANN401)

src/kleinanzeigen_bot/__init__.py

1068-1068: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: analyze
  • GitHub Check: build (macos-latest, 3.14, true)
  • GitHub Check: build (windows-latest, 3.14, true)
  • GitHub Check: build (ubuntu-latest, 3.14, true)
  • GitHub Check: build (windows-latest, 3.10, false)
  • GitHub Check: build (macos-latest, 3.10, false)
  • GitHub Check: build (macos-15-intel, 3.10, false)
  • GitHub Check: build (macos-15-intel, 3.14, true)
  • GitHub Check: build (ubuntu-latest, 3.10, false)

@codecov
Copy link

codecov bot commented Nov 13, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.67%. Comparing base (c7733eb) to head (e94e8b0).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #679      +/-   ##
==========================================
+ Coverage   84.24%   84.67%   +0.42%     
==========================================
  Files          20       20              
  Lines        2019     2042      +23     
  Branches      393      396       +3     
==========================================
+ Hits         1701     1729      +28     
+ Misses        182      177       -5     
  Partials      136      136              
Flag Coverage Δ
integration-tests 29.92% <3.84%> (-0.30%) ⬇️
smoke-tests 31.73% <3.84%> (-0.32%) ⬇️
unit-tests 84.57% <100.00%> (+0.42%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/kleinanzeigen_bot/utils/web_scraping_mixin.py 93.47% <100.00%> (+1.19%) ⬆️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c7733eb...e94e8b0. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@1cu
Copy link
Contributor

1cu commented Nov 14, 2025

Added new unit tests covering the updated combobox selection. The patch below can be applied locally if pushes are disabled.

From be37fd8516baf2db1ea508aa35041450160464f3 Mon Sep 17 00:00:00 2001
From: Jens <[email protected]>
Date: Fri, 14 Nov 2025 14:19:12 +0100
Subject: [PATCH] test: cover combobox selection

---
 tests/unit/test_web_scraping_mixin.py | 56 +++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/tests/unit/test_web_scraping_mixin.py b/tests/unit/test_web_scraping_mixin.py
index d205a3d..989e0a3 100644
--- a/tests/unit/test_web_scraping_mixin.py
+++ b/tests/unit/test_web_scraping_mixin.py
@@ -156,6 +156,62 @@ class TestWebScrapingErrorHandling:
         with pytest.raises(Exception, match = "Cannot clear input"):
             await web_scraper.web_input(By.ID, "test-id", "test text")
 
+    @pytest.mark.asyncio
+    async def test_web_select_combobox_missing_dropdown_options(self, web_scraper:WebScrapingMixin) -> None:
+        """Test combobox selection when aria-controls attribute is missing."""
+        input_field = AsyncMock(spec = Element)
+        input_field.attrs = {}
+        input_field.clear_input = AsyncMock()
+        input_field.send_keys = AsyncMock()
+        web_scraper.web_find = AsyncMock(return_value = input_field)
+
+        with pytest.raises(TimeoutError, match = "Cannot locate combobox dropdown options."):
+            await web_scraper.web_select_combobox(By.ID, "combo-id", "Option", timeout = 0.1)
+
+        input_field.clear_input.assert_awaited_once()
+        input_field.send_keys.assert_awaited_once_with("Option")
+
+    @pytest.mark.asyncio
+    async def test_web_select_combobox_selects_matching_option(self, web_scraper:WebScrapingMixin) -> None:
+        """Test combobox selection matches a visible <li> option."""
+        input_field = AsyncMock(spec = Element)
+        input_field.attrs = {"aria-controls": "dropdown-id"}
+        input_field.clear_input = AsyncMock()
+        input_field.send_keys = AsyncMock()
+
+        dropdown_elem = AsyncMock(spec = Element)
+        dropdown_elem.apply = AsyncMock(return_value = True)
+
+        web_scraper.web_find = AsyncMock(side_effect = [input_field, dropdown_elem])
+        web_scraper.web_sleep = AsyncMock()
+
+        result = await web_scraper.web_select_combobox(By.ID, "combo-id", "Visible Label")
+
+        assert result is dropdown_elem
+        input_field.clear_input.assert_awaited_once()
+        input_field.send_keys.assert_awaited_once_with("Visible Label")
+        dropdown_elem.apply.assert_awaited_once()
+        assert web_scraper.web_sleep.await_count == 2
+
+    @pytest.mark.asyncio
+    async def test_web_select_combobox_no_matching_option_raises(self, web_scraper:WebScrapingMixin) -> None:
+        """Test combobox selection raises when no <li> matches the entered text."""
+        input_field = AsyncMock(spec = Element)
+        input_field.attrs = {"aria-controls": "dropdown-id"}
+        input_field.clear_input = AsyncMock()
+        input_field.send_keys = AsyncMock()
+
+        dropdown_elem = AsyncMock(spec = Element)
+        dropdown_elem.apply = AsyncMock(return_value = False)
+
+        web_scraper.web_find = AsyncMock(side_effect = [input_field, dropdown_elem])
+        web_scraper.web_sleep = AsyncMock()
+
+        with pytest.raises(TimeoutError, match = "Cannot locate combobox dropdown options."):
+            await web_scraper.web_select_combobox(By.ID, "combo-id", "Missing Label")
+
+        dropdown_elem.apply.assert_awaited_once()
+
     @pytest.mark.asyncio
     async def test_web_open_timeout(self, web_scraper:WebScrapingMixin, mock_browser:AsyncMock) -> None:
         """Test page load timeout in web_open."""
-- 
2.50.1 (Apple Git-155)

@bjspi
Copy link
Author

bjspi commented Nov 14, 2025

@1cu
Thanks Jens for the added Diff for the Testcases.
Committed them into the PR, hope that's what I was supposed to do.

So hopefully the PR is complete now and can be merged into the official repo and finally into the release.
Thanks!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65c2980 and 114b242.

📒 Files selected for processing (1)
  • tests/unit/test_web_scraping_mixin.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: TESTING RULES:

  1. NEVER access live website in tests (bot detection risk)
  2. Use @patch for web operations in tests
  3. Use test fixtures for browser automation
  4. Test Pydantic models without web scraping
  5. Mock all web operations in tests
  6. Use pytest markers: unit, integration, smoke
  7. Unit tests: fast, isolated, no external dependencies
  8. Integration tests: use mocks, test with external dependencies
  9. Smoke tests: critical path, no mocks, no browser (NOT E2E tests)
  10. All test code must be in English
  11. Test observable behavior, not implementation
  12. Use fakes/dummies instead of mocks in smoke tests
  13. Focus on minimal health checks, not full user workflows
  14. Include SPDX license headers
  15. Use descriptive test names in English

Files:

  • tests/unit/test_web_scraping_mixin.py
🧬 Code graph analysis (1)
tests/unit/test_web_scraping_mixin.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (4)
  • web_find (655-698)
  • web_select_combobox (884-944)
  • By (71-77)
  • web_sleep (781-785)
🪛 Ruff (0.14.4)
tests/unit/test_web_scraping_mixin.py

168-168: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)


210-210: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)

🔇 Additional comments (1)
tests/unit/test_web_scraping_mixin.py (1)

174-194: LGTM!

This test correctly validates the success path: input field is cleared, text is sent, the dropdown element is located via aria-controls, the JavaScript selection is applied, and web_sleep is called twice as expected. The assertion on web_sleep.await_count == 2 properly verifies the production code's behavior.

@1cu
Copy link
Contributor

1cu commented Nov 14, 2025

@bjspi Please address the merge conflict and the passing of the timeout.

When lints and tests pass I will approve this PR.

@bjspi
Copy link
Author

bjspi commented Nov 15, 2025

@bjspi Please address the merge conflict and the passing of the timeout.

When lints and tests pass I will approve this PR.

Done

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
tests/unit/test_web_scraping_mixin.py (2)

178-181: Escape the period in regex match.

Use a raw string so the trailing period is matched literally.

-        with pytest.raises(TimeoutError, match="Cannot locate combobox dropdown options."):
+        with pytest.raises(TimeoutError, match=r"Cannot locate combobox dropdown options\."):

220-222: Escape the period in regex match (again).

Apply the same fix here:

-        with pytest.raises(TimeoutError, match="Cannot locate combobox dropdown options."):
+        with pytest.raises(TimeoutError, match=r"Cannot locate combobox dropdown options\."):
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bcec1f6 and 67918d9.

⛔ Files ignored due to path filters (1)
  • src/kleinanzeigen_bot/resources/translations.de.yaml is excluded by none and included by none
📒 Files selected for processing (3)
  • src/kleinanzeigen_bot/__init__.py (2 hunks)
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py (1 hunks)
  • tests/unit/test_web_scraping_mixin.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: TESTING RULES:

  1. NEVER access live website in tests (bot detection risk)
  2. Use @patch for web operations in tests
  3. Use test fixtures for browser automation
  4. Test Pydantic models without web scraping
  5. Mock all web operations in tests
  6. Use pytest markers: unit, integration, smoke
  7. Unit tests: fast, isolated, no external dependencies
  8. Integration tests: use mocks, test with external dependencies
  9. Smoke tests: critical path, no mocks, no browser (NOT E2E tests)
  10. All test code must be in English
  11. Test observable behavior, not implementation
  12. Use fakes/dummies instead of mocks in smoke tests
  13. Focus on minimal health checks, not full user workflows
  14. Include SPDX license headers
  15. Use descriptive test names in English

Files:

  • tests/unit/test_web_scraping_mixin.py
src/kleinanzeigen_bot/**/*.py

⚙️ CodeRabbit configuration file

src/kleinanzeigen_bot/**/*.py: CRITICAL RULES FOR KLEINANZEIGEN BOT:

  1. ALL code, comments, and text MUST be in English
  2. User-facing messages MUST use translation system (_()) function
  3. NEVER access live website in tests (bot detection risk)
  4. Use WebScrapingMixin for browser automation
  5. Handle TimeoutError for all web operations
  6. Use ensure() for critical validations
  7. Don't add features until explicitly needed
  8. Keep solutions simple and straightforward
  9. Use async/await for I/O operations
  10. Follow Pydantic model patterns
  11. Use proper error handling and logging
  12. Test business logic separately from web scraping
  13. Include SPDX license headers on all Python files
  14. Use type hints for all function parameters and return values
  15. Use structured logging with context

Files:

  • src/kleinanzeigen_bot/__init__.py
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
🧬 Code graph analysis (2)
tests/unit/test_web_scraping_mixin.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (4)
  • web_find (729-745)
  • web_select_combobox (1002-1062)
  • By (75-81)
  • web_sleep (899-903)
src/kleinanzeigen_bot/__init__.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (3)
  • web_find (729-745)
  • By (75-81)
  • web_select_combobox (1002-1062)
🪛 Ruff (0.14.4)
tests/unit/test_web_scraping_mixin.py

178-178: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)


220-220: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)

src/kleinanzeigen_bot/__init__.py

1077-1077: Avoid specifying long messages outside the exception class

(TRY003)

src/kleinanzeigen_bot/utils/web_scraping_mixin.py

1002-1002: Dynamically typed expressions (typing.Any) are disallowed in selected_value

(ANN401)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build (macos-15-intel, 3.14, true)
  • GitHub Check: build (windows-latest, 3.14, true)
  • GitHub Check: build (windows-latest, 3.10, false)
  • GitHub Check: build (macos-15-intel, 3.10, false)
  • GitHub Check: analyze
🔇 Additional comments (2)
src/kleinanzeigen_bot/__init__.py (1)

1087-1090: Combobox routing looks good.

Detection by type="text" and role="combobox" and delegating to web_select_combobox aligns with the new helper.

tests/unit/test_web_scraping_mixin.py (1)

185-205: Async combobox selection test is solid.

Covers typing, matching, return value, and pauses; mocks are isolated and fast.

1cu and others added 2 commits November 18, 2025 10:26
This commit addresses all review comments and CodeRabbit suggestions:

- Fix combobox type annotation (Any -> str | int) to resolve ANN401
- Update docstring to accurately describe combobox behavior
- Add distinct error messages for different failure scenarios
- Honor timeout parameter in web_select (pass to web_find)
- Add exception handling to convert JS errors to TimeoutError
- Localize error message in __set_special_attributes
- Add test for special character handling (quotes, backslashes)
- Update translations for all new error messages

Changes improve error clarity, type safety, and maintainability.

Co-authored-by: Claude <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (1)

964-1004: web_select selection logic is solid; tighten types and docstring.

The new two-phase JS selection (by option.value then by trimmed label), combined with JSON escaping and normalization to a localized TimeoutError, is a good, maintainable improvement and correctly honors the caller’s timeout.

Two small polish points:

  • selected_value: Any can likely be narrowed to str | int (mirroring web_input / web_select_combobox and avoiding ANN401-style “Any” usage).
  • The docstring still advertises UnexpectedTagNameException, which is never raised here; consider either removing that or explicitly checking the tag name and raising a custom error.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67918d9 and c311b45.

⛔ Files ignored due to path filters (1)
  • src/kleinanzeigen_bot/resources/translations.de.yaml is excluded by none and included by none
📒 Files selected for processing (3)
  • src/kleinanzeigen_bot/__init__.py (2 hunks)
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py (1 hunks)
  • tests/unit/test_web_scraping_mixin.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/kleinanzeigen_bot/**/*.py

⚙️ CodeRabbit configuration file

src/kleinanzeigen_bot/**/*.py: CRITICAL RULES FOR KLEINANZEIGEN BOT:

  1. ALL code, comments, and text MUST be in English
  2. User-facing messages MUST use translation system (_()) function
  3. NEVER access live website in tests (bot detection risk)
  4. Use WebScrapingMixin for browser automation
  5. Handle TimeoutError for all web operations
  6. Use ensure() for critical validations
  7. Don't add features until explicitly needed
  8. Keep solutions simple and straightforward
  9. Use async/await for I/O operations
  10. Follow Pydantic model patterns
  11. Use proper error handling and logging
  12. Test business logic separately from web scraping
  13. Include SPDX license headers on all Python files
  14. Use type hints for all function parameters and return values
  15. Use structured logging with context

Files:

  • src/kleinanzeigen_bot/__init__.py
  • src/kleinanzeigen_bot/utils/web_scraping_mixin.py
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: TESTING RULES:

  1. NEVER access live website in tests (bot detection risk)
  2. Use @patch for web operations in tests
  3. Use test fixtures for browser automation
  4. Test Pydantic models without web scraping
  5. Mock all web operations in tests
  6. Use pytest markers: unit, integration, smoke
  7. Unit tests: fast, isolated, no external dependencies
  8. Integration tests: use mocks, test with external dependencies
  9. Smoke tests: critical path, no mocks, no browser (NOT E2E tests)
  10. All test code must be in English
  11. Test observable behavior, not implementation
  12. Use fakes/dummies instead of mocks in smoke tests
  13. Focus on minimal health checks, not full user workflows
  14. Include SPDX license headers
  15. Use descriptive test names in English

Files:

  • tests/unit/test_web_scraping_mixin.py
🧬 Code graph analysis (2)
src/kleinanzeigen_bot/__init__.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (3)
  • web_find (729-745)
  • By (75-81)
  • web_select_combobox (1006-1070)
tests/unit/test_web_scraping_mixin.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (4)
  • web_find (729-745)
  • web_select_combobox (1006-1070)
  • By (75-81)
  • web_sleep (899-903)
🔇 Additional comments (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (1)

1006-1070: web_select_combobox implementation matches requirements and tests.

  • Respects optional timeout, reusing the timeout for both input and dropdown lookups.
  • Handles missing aria-controls and no-matching-
  • cases with concise, localized TimeoutErrors and appropriate logging.
  • Uses JSON-escaped value and a normalization function in JS to robustly match displayed labels (whitespace/case tolerant), and returns the dropdown element after a second sleep.

No changes needed here from my side.

Addresses final review feedback:

- Localize all error messages in __set_special_attributes using _()
- Add web_sleep assertions to combobox tests for consistency
- Add tests for web_select to improve patch coverage:
  - test_web_select_by_value: successful option selection
  - test_web_select_raises_on_missing_option: error handling

These changes improve i18n compliance and increase test coverage
from 69.23% to ensure proper exception handling in web_select.

All 527 tests passing (+2 new tests).
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20b7835 and 4c14eb3.

📒 Files selected for processing (2)
  • src/kleinanzeigen_bot/__init__.py (1 hunks)
  • tests/unit/test_web_scraping_mixin.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/kleinanzeigen_bot/**/*.py

⚙️ CodeRabbit configuration file

src/kleinanzeigen_bot/**/*.py: CRITICAL RULES FOR KLEINANZEIGEN BOT:

  1. ALL code, comments, and text MUST be in English
  2. User-facing messages MUST use translation system (_()) function
  3. NEVER access live website in tests (bot detection risk)
  4. Use WebScrapingMixin for browser automation
  5. Handle TimeoutError for all web operations
  6. Use ensure() for critical validations
  7. Don't add features until explicitly needed
  8. Keep solutions simple and straightforward
  9. Use async/await for I/O operations
  10. Follow Pydantic model patterns
  11. Use proper error handling and logging
  12. Test business logic separately from web scraping
  13. Include SPDX license headers on all Python files
  14. Use type hints for all function parameters and return values
  15. Use structured logging with context

Files:

  • src/kleinanzeigen_bot/__init__.py
tests/**/*.py

⚙️ CodeRabbit configuration file

tests/**/*.py: TESTING RULES:

  1. NEVER access live website in tests (bot detection risk)
  2. Use @patch for web operations in tests
  3. Use test fixtures for browser automation
  4. Test Pydantic models without web scraping
  5. Mock all web operations in tests
  6. Use pytest markers: unit, integration, smoke
  7. Unit tests: fast, isolated, no external dependencies
  8. Integration tests: use mocks, test with external dependencies
  9. Smoke tests: critical path, no mocks, no browser (NOT E2E tests)
  10. All test code must be in English
  11. Test observable behavior, not implementation
  12. Use fakes/dummies instead of mocks in smoke tests
  13. Focus on minimal health checks, not full user workflows
  14. Include SPDX license headers
  15. Use descriptive test names in English

Files:

  • tests/unit/test_web_scraping_mixin.py
🧬 Code graph analysis (2)
src/kleinanzeigen_bot/__init__.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (6)
  • web_find (729-745)
  • By (75-81)
  • web_select (956-1004)
  • web_click (636-646)
  • web_select_combobox (1006-1070)
  • web_input (852-863)
tests/unit/test_web_scraping_mixin.py (1)
src/kleinanzeigen_bot/utils/web_scraping_mixin.py (7)
  • web_find (729-745)
  • web_sleep (899-903)
  • web_select_combobox (1006-1070)
  • By (75-81)
  • web_check (589-634)
  • web_await (560-587)
  • web_select (956-1004)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: build (windows-latest, 3.14, true)
  • GitHub Check: build (macos-15-intel, 3.14, true)
  • GitHub Check: build (macos-15-intel, 3.10, false)
  • GitHub Check: build (macos-latest, 3.10, false)
  • GitHub Check: build (ubuntu-latest, 3.14, true)
  • GitHub Check: analyze
  • GitHub Check: build (ubuntu-latest, 3.10, false)
  • GitHub Check: build (macos-latest, 3.14, true)
  • GitHub Check: build (windows-latest, 3.10, false)
🔇 Additional comments (2)
src/kleinanzeigen_bot/__init__.py (1)

1068-1077: ID fallback and localized TimeoutError are aligned with guidelines

The fallback from name-based XPath to ID lookup is straightforward and the raised TimeoutError now uses _() with a short English message, which matches the project’s translation and simplicity rules. No changes needed here.

tests/unit/test_web_scraping_mixin.py (1)

170-281: Combobox and select tests accurately cover the new behaviors

The new async tests for web_select_combobox and web_select:

  • Exercise all key paths (missing aria-controls, successful option click, no matching <li>, special-character labels, select-by-value success, and missing option error).
  • Correctly stub web operations (web_find, web_sleep, web_check, web_await, Element.apply) without touching real browsers or networks.
  • Assert meaningful observables (sleep counts, sent text, apply calls, and localized TimeoutError messages) while keeping the setup simple.

This is a solid, maintainable test addition that mirrors the mixin’s logic well.

The exception handler at line 1094 previously logged "is not of kind
radio button" which was misleading since the try block actually
attempts select, checkbox, combobox, and text input.

Changed to generic message "Failed to set attribute field '%s' via
known input types." to accurately reflect the failure condition.

Added German translation for the new message.
@1cu
Copy link
Contributor

1cu commented Nov 18, 2025

@bjspi Please confirm that the current state is still working for you.

@1cu
Copy link
Contributor

1cu commented Nov 18, 2025

@Heavenfighter since I committed last, can you please approve this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Development

Successfully merging this pull request may close these issues.

2 participants