mirror of
https://github.com/Second-Hand-Friends/kleinanzeigen-bot.git
synced 2026-07-09 12:41:05 +02:00
835e78f5471f27180b25bb1bd3be33a0758bb357
225 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f2da0355d5 |
fix: classify Auth0 IP range block (#1188)
## ℹ️ Description - Link to the related issue(s): Issue #1120 - Classifies Kleinanzeigen IP-range block pages explicitly when Auth0 post-submit verification remains on the password URL. - This makes the failure actionable without changing login/captcha behavior or attempting to bypass the block. ## 📋 Changes Summary - Add `IP_RANGE_BLOCKED` to post-submit Auth0 state classification when the password-page DOM contains the Kleinanzeigen IP-range block heading. - Keep existing classification facts such as `STILL_ON_PASSWORD_PAGE`, so combined states remain visible. - Keep detection scoped to the Auth0 password page and best-effort. - Add focused unit tests for detection, gating, resilience, and coexistence with Auth0 inline errors. - No new dependencies, configuration changes, schema changes, or translation updates. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test tests/unit/test_login_flow.py tests/unit/test_translations.py`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved sign-in diagnostics to detect when login is stalled on the password step due to an IP-range restriction. * Added support for preserving existing login-state facts when the IP-block page probe fails, and for correctly combining IP-block signals with inline error states. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ce0c5fa5b5 |
fix: handle DOM retry and button combobox selection (#1186)
## ℹ️ Description - Link to the related issue(s): Issue #1184 - Fixes two publish-flow failures reported in issue #1184: - persistent nodriver `ProtocolException(code=-32601)` could spin forever in `web_await()` while repeatedly re-attaching the CDP session; - special-attribute button comboboxes in the Handy & Telefon category needed open/read/select to happen atomically in one browser execution. ## 📋 Changes Summary - Bound `web_await()` CDP reattach retries by the configured timeout and add a short nonzero retry delay to avoid event-loop starvation. - Update special-attribute button-combobox selection to use one async `web_execute()` script that opens via pointer/mouse events, discovers options, matches API values, and clicks the selected option before the dropdown can close. - Add regression tests for bounded `-32601` retry behavior and atomic button-combobox selection. - Update the German translation for the enhanced combobox failure message. - Verified with the live `button-combobox` DOM probe. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. ## Validation - `pdm run format` - `pdm run lint` - `pdm run test` — 1370 passed, 4 skipped - `pdm run python data/verify_dom_assumptions.py run --yes --probe button-combobox --report data/.temp/dom-assumptions-issue-1184-button-combobox.json` — passed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved dropdown/combobox option selection with stronger matching and more informative timeout/error reporting. * Tightened CDP re-attach retry logic to strictly follow the remaining timeout budget. * Added a startup runtime warning if the required nodriver CDP re-attach patch isn’t detected. * **Documentation / Chores** * Updated source installation guidance (pip-only users) to ensure the nodriver patch is applied. * Made the nodriver fix script marker-based and idempotent, including normalization of legacy patched installs. * **Tests** * Expanded unit tests for combobox selection, re-attach timeout/bounds behavior, CLI patch warning behavior, and the nodriver fix script. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
27b47f7561 |
fix: capture Auth0 post-submit diagnostics (#1183)
## ℹ️ Description - Link to the related issue(s): Issue #1120 - Capture diagnostics when Auth0 post-submit verification remains inconclusive, so future failures include a DOM/screenshot bundle plus classified metadata. ## 📋 Changes Summary - Reuse existing login-detection diagnostics for the final Auth0 post-password-submit timeout. - Add sanitized JSON metadata with `event`, `classification`, and `page_url`. - Keep diagnostics best-effort so capture failures do not mask the original classified timeout. - Add focused tests for enabled/disabled capture, privacy sanitization, failure preservation, and diagnostics argument forwarding. - No new configuration, schema, translation, captcha, or identifier-flow changes. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test tests/unit/test_login_flow.py tests/unit/test_translations.py`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. (Not needed.) By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved login timeout handling with clearer error messages and safer URL reporting. * Added fallback diagnostics during failed post-login verification, while preserving the original timeout if diagnostics capture fails. * Prevented sensitive URL parts from appearing in error reports. * **Tests** * Expanded coverage for login diagnostics forwarding and timeout behavior with diagnostics enabled and disabled. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4acbc09fac |
fix: classify Auth0 post-submit uncertainty (#1181)
## ℹ️ Description - Link to the related issue(s): Issue #1120 - Adds focused diagnostics/classification for the post-password-submit Auth0 uncertainty path. This keeps the existing login/captcha/identifier flow stable while making the new failure mode easier to classify from logs. ## 📋 Changes Summary - Classifies final post-submit uncertainty as still on the Auth0 password page, visible Auth0 error text, verification/MFA-like state, or unknown redirect/session inconclusive state. - Logs sanitized diagnostic context and raises clearer classified messages while preserving the existing timeout prefix. - Adds focused unit tests for the new classifications and snippet sanitization. - Updates German translations for new translatable messages. - No new dependencies or configuration changes. ### ⚙️ Type of Change - [x] 🐞 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: - [x] I have reviewed my changes to ensure they meet the project standards. - [x] I have tested my changes and ensured that all tests pass (pdm run test). - [x] I have formatted the code (pdm run format). - [x] I have verified that linting passes (pdm run lint). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved handling when transitioning after Auth0 password submission by using safer, sanitized diagnostic URLs. * Added clearer, classification-aware timeout messages when the next verification step can’t be identified. * Strengthened detection of post-Auth0 states, including inline Auth0 errors and MFA/verification prompts, with more graceful degradation on probing failures. * **Tests** * Expanded unit tests for post-submit state classification, URL sanitization, and improved timeout/error-message expectations, including resilience to probe/URL retrieval failures. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1c34da9298 |
feat: show APR status indicators (#1180)
## ℹ️ Description - Link to the related issue(s): Issue # - Adds display-only APR indicators to the local status table so users can see repost/update APR state without running publish/update flows. ## 📋 Changes Summary - Adds optional `APR repost` and `APR update` columns to `kleinanzeigen-bot status` when APR is effectively enabled for at least one active ad. - Reuses existing `evaluate_auto_price_reduction()` logic for REPLACE and MODIFY modes; does not apply or mutate APR decisions. - Keeps APR output plain while preserving existing status color behavior. - Adds German translations and unit coverage for rendering, evaluator call modes, guardrails, and mutation safety. - No new dependencies or configuration changes. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [ ] 🐞 Bug fix (non-breaking change which fixes an issue) - [x] ✨ 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`: 1344 passed, 4 skipped). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Status tables now show optional APR-related columns for repost and update pricing decisions when available. * APR results are displayed in a compact, easy-to-read format such as due, not due, error, or off. * **Bug Fixes** * Improved status table layout and spacing so translated status text fits more reliably. * Status summaries and row rendering now handle coloured and uncoloured cells more consistently. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
25a379fe67 |
feat: colorize status output (#1178)
## ℹ️ Description - Link to the related issue(s): Issue # - Adds optional color to the `kleinanzeigen-bot status` table using the existing Colorama dependency. - Keeps default status behavior local/read-only and gates ANSI output via terminal/env policy. ## 📋 Changes Summary - Adds `utils.color.should_use_color()` with `NO_COLOR` > `FORCE_COLOR` > TTY detection. - Colors only the status column while preserving plain ASCII layout and summary output. - Wires status rendering to the color gate in `_handle_status()`. - Removes legacy `colorama.init()` from `update_checker.py`; package init already enables Windows ANSI support. - Adds unit tests for color gating and colored status rendering. - No new dependencies or configuration changes. ### ⚙️ Type of Change - [ ] 🐞 Bug fix (non-breaking change which fixes an issue) - [x] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`: 1332 passed, 4 skipped). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Status output can now be shown with optional terminal colors for improved readability. * Color display automatically follows your terminal and environment settings. * **Bug Fixes** * Status tables keep proper alignment even when colored text is enabled. * Unknown statuses remain uncolored, avoiding inconsistent display. * **Tests** * Added coverage for color handling and status rendering to improve reliability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
50a4b570cf |
feat: add ad status command (#1177)
## ℹ️ Description - Link to the related issue(s): Issue # - Adds a CLI status overview inspired by useful lifecycle features from kleinanzeigen-bot-ui, while keeping this project CLI-only. - The new command helps users see local ad state at a glance without replacing the existing verify/APR preflight flow. ## 📋 Changes Summary - Add `kleinanzeigen-bot status` with an ASCII table and summary for local ad states: disabled, draft, changed, due, and published-local. - Add shared pure helpers for content-change and republication-due checks. - Add a small neutral `load_ad_configs()` loader reused by `load_ads()` and `status` without broad loader redesign. - Add German translations and focused unit/guardrail tests. - No schema/config model changes or generated artifacts. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [ ] 🐞 Bug fix (non-breaking change which fixes an issue) - [x] ✨ 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. Validation: - `pdm run format` passed - `pdm run lint` passed - `pdm run test`: 1315 passed, 4 skipped; coverage 93.18% <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a new `status` command to view the current state of configured ads. * Introduced a status summary that shows whether ads are disabled, drafts, changed, due, or published locally. * Added cleaner handling for ad loading and republication checks, improving consistency in displayed results. * **Bug Fixes** * Improved change detection for ads, including better handling when stored data is missing or empty. * Enhanced due-date checks so timing-based status is more reliable. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d5d596d481 |
fix: target visible Auth0 submit button and broaden captcha detection (#1120) (#1176)
## ℹ️ Description Fixes #1120 — Auth0 login fails after email submit because the bot clicks a hidden submit button and cannot detect non-reCAPTCHA captcha providers. **Root causes:** 1. **Hidden submit button** — Auth0 renders two `button[type='submit']` elements. The hidden one (`class=ulp-hidden-form-submit-button`, `aria-hidden=true`) appears first in DOM. `querySelector()` was matching it instead of the visible primary button. 2. **Captcha detection too narrow** — Only matched reCAPTCHA v2 iframes. Kleinanzeigen.de uses Cloudflare Turnstile via Auth0 v2 (`data-captcha-provider='auth0_v2'`), which renders inline without an iframe. **Evidence:** @jochenberger provided HTML from the identifier page confirming both issues. User CAN manually click 'Weiter' and login succeeds. ## 📋 Changes Summary - Add `_click_auth0_submit()` helper using `[data-action-button-primary='true']` to target the visible button - Broaden captcha detection: iframe selector covers 6 providers, container selector uses `[data-captcha-provider]` + substring class matching - Simplify `handle_identifier_captcha_state()` with Turnstile token wait via JS property check - Use `_click_auth0_submit()` for both identifier and password submits - Add Auth0 provider provenance comments to selectors ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved captcha/challenge detection to support both iframe-based providers and Auth0 container-based challenges. - Updated the Auth0 login flow to click the correct visible Auth0 primary submit button, avoiding hidden controls for identifier and password steps. - Centralized captcha detection timeout to make challenge handling more consistent. - **Tests** - Expanded and tightened unit tests for the Auth0 identifier-step captcha/submit behavior, including selector expectations and control-flow scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8e70d0411d |
refactor: decompose WebScrapingMixin (static helpers, diagnostics, exception annotations) (#1174)
## ℹ️ Description Decompose `WebScrapingMixin` — incremental internal refactors to reduce class size and improve separation of concerns. ## 📋 Changes Summary ### Static method extraction - Extract 5 `@staticmethod` members from `WebScrapingMixin` to module-level private functions in `web_scraping_mixin.py` - Update internal call sites and test call sites (no compatibility aliases) ### Browser diagnostics extraction - Create `src/kleinanzeigen_bot/utils/browser_diagnostics.py` with module-level diagnostic functions - `WebScrapingMixin.diagnose_browser_issues()` → thin delegator (resolves `self` attrs, calls extracted logic) - Replace `self`-coupled logic with explicit parameter dependencies - Migrate translations to new module section in `translations.de.yaml` ### Exception annotations - Add `# noqa: BLE001` + justification comments to 4 bare `except Exception:` blocks (no behavior changes) ### ⚙️ Type of Change - [x] 🔧 Refactor (internal code improvements, no behavior changes) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`) — 1287 passed, 4 skipped - [x] I have formatted the code (`pdm run format`) - [x] I have verified that linting passes (`pdm run lint`) — ruff, mypy, pyright clean - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added enhanced browser troubleshooting with remote-debugging connectivity checks, running-process inspection, and Chrome/Edge compatibility validation including recommended configuration updates. * **Refactor** * Reorganized browser/session diagnostics and remote-debugging argument handling into dedicated utilities; introduced a shared browser runtime configuration container. * **Chores** * Suppressed linter warnings for intentionally broad exception handling. * **Tests** * Updated unit and integration tests to match the new diagnostics flow and remote-debugging probing behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b2dce90ef1 |
refactor: remove dead wrapper methods from KleinanzeigenBot (#1172)
## ℹ️ Description Remove four private wrapper methods on `KleinanzeigenBot` in `app.py` that wrap public module-level functions but have zero production call sites. This is a cleanup-only change with no behavioral impact. ## 📋 Changes Summary - **Remove** `_has_logged_in_marker` — delegates to `login_flow.has_logged_in_marker` - **Remove** `_check_publishing_result` — delegates to `publishing_workflow.check_publishing_result` - **Remove** `_delete_old_ad_if_needed` — delegates to `publishing_workflow.delete_old_ad_if_needed` - **Remove** `_capture_login_detection_diagnostics_if_enabled` — delegates to `login_flow.capture_login_detection_diagnostics_if_enabled` - **Remove** unused `Literal` import - **Retarget** 4 helper-detail tests that called the deleted wrapper directly to the real `login_flow.capture_login_detection_diagnostics_if_enabled` function ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Reorganized login detection diagnostics functionality for improved accessibility * Simplified login status verification logic within the authentication system <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1f142f8339 |
refactor: extract selector and filter helpers from load_ads (#1169)
## ℹ️ Description Extract three private helpers from `load_ads()` to remove the last remaining `# noqa: PLR0915` annotation in main source. `load_ads` goes from ~100 statements to ~35 — a clean orchestration function. ## 📋 Changes Summary - **`_parse_ad_selector`**: parse `ads_selector` string into numeric IDs list or frozenset of tokens - **`_should_include_ad`**: token-based ad filtering ("changed"/"new"/"due"/"all") — preserves exact ordering, side effects, and "changed"/price-reduction-for-update logic - **`_prepare_selected_ad_entry`**: description validation, category resolution, image resolution — called only after filtering to avoid validating skipped ads - Translation keys moved to match new caller function names (`_should_include_ad:`, `_prepare_selected_ad_entry:`) - Added regression test: inactive ad with unresolvable images is skipped without error ### ⚙️ Type of Change - [x] ✨ New feature (adds new functionality without breaking existing usage) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Reorganized ad loading process with improved selector parsing and filtering logic for better code maintainability. * Optimized validation workflow: inactive and non-selected ads now skip description and image validation, improving overall efficiency. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
eaa646874d |
refactor: replace get_message_template match with dict lookup (#1165)
## ℹ️ Description Replace the ~115-arm match/case in `__get_message_template` with a module-level `_MESSAGE_TEMPLATES` dict of raw English strings plus a 3-line lazy `_(template)` lookup function. - **Motivation**: Eliminate 5 complexity `noqa` annotations (C901, PLR0911, PLR0912, PLR0915, E701) by removing the real architectural complexity — a pure flat error-code → message mapping that has zero branching or logic. - **Approach**: A `dict[str, str]` of raw English strings (no `_()` calls at import time) with lazy translation inside `__get_message_template`. `"custom_error"` intentionally omitted — handled by `.get()` default. - **Translation**: Caller function name unchanged → translation keys under `__get_message_template` preserved. AST-based translation test extractor updated to recognize `_MESSAGE_TEMPLATES` dict values. ## 📋 Changes Summary - `src/kleinanzeigen_bot/utils/pydantics.py`: Replaced ~115 match arms with `_MESSAGE_TEMPLATES` dict + 3-line function; removed all 5 noqa annotations - `tests/unit/test_translations.py`: Added AST extraction for `_MESSAGE_TEMPLATES` dict values to maintain translation coverage checks ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved error message handling system to support better internationalization and translation workflows * **Tests** * Updated test suite to extract and verify translatable message strings <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
32c6d3985f |
refactor: split published ads pagination (#1164)
## ℹ️ Description Extract content-decoding and paging-navigation logic from `fetch_published_ads` into focused module-level helpers, reducing function complexity while preserving all behavior. ## 📋 Changes Summary - **`published_ads.py`**: Extracted `_parse_published_ads_page()` (content decode, JSON parse, payload validation, ad filtering) and `_determine_next_page()` (paging metadata resolution, last-page/empty-ads guards). Main `fetch_published_ads` is now a clean orchestration loop: request → response check → parse → extend → paginate. - **`translations.de.yaml`**: Split the single `fetch_published_ads:` translation section into three sections (`fetch_published_ads:`, `_parse_published_ads_page:`, `_determine_next_page:`) matching the new caller function names. Added missing translation for "No paging dict found on page %s". - **`pyproject.toml`**: Lowered `max-statements` from 115 to 90. - **`pydantics.py`**: Added `PLR0915` noqa for `__get_message_template` (already an approved exception for the generated pydantic error-code mapping). ### ⚙️ Type of Change - [x] 🐞 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 - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved published-ads fetching by separating response decoding and pagination handling into dedicated helpers, while preserving existing behavior. * Reworked strict-mode validation and stopping conditions for clearer control flow and more consistent error handling. * **Chores** * Updated linting limits to enforce stricter code quality. * Added targeted lint suppression for an overly complex section. * **Tests** * Expanded unit test coverage for published-ads content decoding and strict-mode failure cases across multiple malformed/edge responses. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
c6843a49c0 |
fix: avoid TypeError abort when web_request gets a non-dict response (#1162)
## ℹ️ Description When publishing a batch of ads, a single transient page-load failure could crash the **entire** run with an unhandled `TypeError`. During the pre-publish delete step, `delete_flow.delete_ad` calls `web_request`, which runs a `fetch(...)` via `web_execute`. When the page context is torn down mid-navigation (the same condition that surfaces as *"Page did not finish loading within 30.0 seconds"*), `web_execute` returns a CDP `ExceptionDetails` object (or `None`) instead of the expected response dict. `web_request` then does `response["statusCode"]`, raising: ``` TypeError: [ExceptionDetails] object is not subscriptable File ".../utils/web_scraping_mixin.py", line 1537, in web_request response["statusCode"] in valid_response_codes, ``` This `TypeError` is **not** caught by the publish retry loop (which handles `TimeoutError` / `ProtocolException`), so it propagates all the way up and aborts the whole batch — every remaining ad goes unpublished. Observed live while publishing ~25 ads: 9 published successfully, two hit transient load timeouts, and the next `delete_ad` request crashed the process. - Link to the related issue(s): Issue # ## 📋 Changes Summary - `web_request`: guard the response — if it is not a `dict` containing `statusCode`, raise a `ProtocolException` (already imported) instead of blindly subscripting it. - `ProtocolException` is already handled by the publish retry/skip logic, so a single transient failure now retries and then skips that one ad, letting the rest of the batch continue. - Add a parametrized regression test in `test_web_scraping_mixin.py` covering `None` / non-dict / missing-`statusCode` responses. ### ⚙️ Type of Change - [x] 🐞 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 - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured the affected tests pass (`pytest tests/unit/test_web_scraping_mixin.py -k web_request` → 7 passed). - [ ] I have formatted the code (`pdm run format`). - [ ] I have verified that linting passes (`pdm run lint`). - [ ] I have updated documentation where necessary (n/a). By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Enhanced validation for network operation responses with improved error messaging for unexpected situations, preventing hidden failures and providing clearer diagnostics when issues occur. * **Tests** * Extended test coverage to comprehensively validate error handling across various edge cases and unexpected response scenarios in network operations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b5e2769f2d |
refactor: extract special attribute helpers from publishing form (#1161)
## ℹ️ Description Extract three module-level helpers from `set_special_attributes()` in `publishing_form.py` to reduce local variable count and improve readability. `set_special_attributes` had 27 locals — after extraction it has 10. - Behavior-preserving: no change to exception propagation, logging, or control flow. - `condition_s` probe stays in main to avoid translation-key churn. ## 📋 Changes Summary - Extract `_build_special_attribute_xpath()` — Pure XPath construction (fix comment: five → six patterns) - Extract `_resolve_special_attribute_element()` — Wraps `web_find_all` + candidate pick, propagates exceptions uncaught - Extract `_set_special_attribute_input()` — 6-branch input-type dispatch; hidden-input returns without logging, caller logs once - Rewire `set_special_attributes` to call the three helpers - Ratchet `pyproject.toml`: PLR0914 max-locals 27 → 20 - Update `translations.de.yaml`: move 4 debug messages + add `Failed to set attribute` under `_set_special_attribute_input` ### ⚙️ Type of Change - [ ] 🐞 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) - [x] ♻️ Refactor (non-breaking restructuring of existing code) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). → 1268 passed, 4 skipped - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. → Translation file updated <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit * **Chores** * Tightened linting rules by lowering the allowed maximum number of local variables. * **Refactor** * Reworked special-attribute DOM handling to use clearer selection and value-setting paths for different field types, including improved fallback behavior. * **Tests** * Added async unit test coverage for `set_special_attributes`, including input fallback behavior and error cases for unsupported checkbox values and missing hidden input metadata. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d542f1ea92 |
feat: use configured --remote-debugging-host for browser diagnostics probes (#1160)
## ℹ️ Description `diagnose_browser_issues()` previously hardcoded `127.0.0.1` for port-open and `/json/version` API probes. When Chrome's remote debugging is exposed on a different host (Docker, LAN, CI), the diagnostics miss the running browser entirely. This PR makes diagnostics host-aware by parsing `--remote-debugging-host` from `browser_config.arguments` and using the configured host instead of the hardcoded `127.0.0.1`. All diagnostics probes (port, API, Chrome version) now respect the configured host. ## 📋 Changes Summary - Added `_diagnostic_remote_debugging_endpoint()` — private diagnostics parser with first-wins semantics for both host and port, defaults to `127.0.0.1:0` - Added `_format_url_host()` — IPv6-safe URL bracketing helper - Threaded configured host into `net.is_port_open()`, `_remote_debugging_api_browser()`, and `_diagnose_chrome_version_issues()` - Emit `LOG.warning` before probing a non-loopback configured host (security/documentation guard) - Updated German translations for changed log message strings - Added tests: configured host `10.0.0.5` and `192.168.1.100`, host-after-port ordering, IPv6 URL formatting, Chrome-version host propagation - Default behavior (no `--remote-debugging-host` argument) unchanged — probes remain `127.0.0.1` ### ⚙️ Type of Change - [ ] 🐞 Bug fix (non-breaking change which fixes an issue) - [x] ✨ New feature (adds new functionality without breaking existing usage) - [ ] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved remote browser debugging diagnostics to work with non-local hosts, including correct IPv6-safe URL formatting. * Added host-aware reachability checks and clearer warnings when the configured remote host may not be loopback. * Enhanced diagnostic messaging to report the configured host and port during remote debugging checks. * **Tests** * Expanded unit test coverage for configured remote-debugging hosts (including IPv6) and updated expectations for host-specific diagnostic outputs. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d384f172b8 |
fix: remove individual shipping publishing (#1159)
## ℹ️ Description - Link to the related issue(s): Issue #1157 - Kleinanzeigen no longer offers individual/custom shipping. This updates publishing, validation, docs, schemas, translations, and tests so existing `shipping_costs` configs are handled gracefully instead of failing against removed DOM controls. ## 📋 Changes Summary - Keep `shipping_costs` for legacy configs/downloaded ads, but mark it deprecated in schema/docs and ignore it during publishing. - Remove publishing interaction with removed individual-shipping DOM controls. - Require predefined non-empty `shipping_options` for `sell_directly`. - Update docs, generated schemas/config artifacts, German translations, and unit tests. - Verified live DOM for `sell_directly: true` + `shipping_type: SHIPPING` + predefined `shipping_options` via local ignored script; no ad submission performed. ### ⚙️ Type of Change Select the type(s) of change(s) included in this pull request: - [x] 🐞 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated shipping guides to deprecate custom individual shipping costs and make publishing depend on per-ad predefined `shipping_options` (DHL/Hermes; selectable options must come from a single size group). * Added migration guidance replacing `shipping_costs` with the correct `shipping_options`. * Clarified “Sell directly” requires `shipping_type: SHIPPING`, non-empty `shipping_options`, and `FIXED`/`NEGOTIABLE` pricing. * **Bug Fixes** * Publishing is blocked when `shipping_costs` is provided without `shipping_options`. * Removed unsupported individual-shipping handling and tightened “Sell directly” eligibility rules. * **Tests** * Expanded unit coverage for deprecation behavior, content handling, and direct-buy validation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5ab4015251 |
refactor: simplify browser diagnostics (#1158)
## ℹ️ Description - Link to the related issue(s): Issue # - Refactor browser diagnostics to reduce the complexity of the diagnostics orchestration while preserving the generated diagnostics report and browser workflow behavior. ## 📋 Changes Summary - Extract factual browser diagnostic checks into focused helpers for binary detection, remote debugging port parsing, remote API probing, target browser name detection, and process discovery. - Keep translated diagnostics logging as direct literal log calls in `diagnose_browser_issues`. - Add regression coverage for remote debugging host handling, invalid ports, first-port precedence, and port `0` pass-through behavior. - No dependencies, configuration changes, or additional requirements introduced. ### ⚙️ 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) - [x] ♻️ Refactor (no user-facing behavior change) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved browser diagnostics by breaking down remote debugging checks into clearer steps, enhancing resilience when probing the browser and inspecting running processes, and refining success vs failure logging. * **Tests** * Expanded unit coverage to ensure remote debugging probing always targets `http://127.0.0.1:<port>`, validates `--remote-debugging-port` parsing (including invalid and multi-argument cases), and skips remote debugging when the port is `0` while continuing Chrome version diagnostics. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
c8fb72ae9e |
refactor: split command orchestration handlers (#1155)
## ℹ️ Description - Link to the related issue(s): Issue # - Refactors the CLI command orchestration boundary so command dispatch remains visible while command execution lives in focused handlers. ## 📋 Changes Summary - Split command execution paths in `KleinanzeigenBot.run()` into named handlers for configuration, verification, update checks, content hashing, and ad workflows. - Preserved literal command dispatch cases and command-specific behavior for update checks, download validation, browser login, and cleanup. - Moved German translation keys to the new handler caller names for existing runtime messages. - Added focused tests for update-check behavior, download validation ordering, workspace state usage, and translation key placement. - Lowered the configured branch and cyclomatic complexity limits now that the command orchestration boundary is simpler. ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Strengthened code quality standards by implementing stricter complexity thresholds in the development configuration. * **Refactor** * Reorganized command handler architecture to improve internal code structure and maintainability. * **Tests** * Significantly expanded test coverage to validate command execution, argument handling, and translation data integrity. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
66cab91898 |
refactor: reduce return-count complexity (#1153)
## ℹ️ Description This refactor reduces return-count complexity for the next PLR0911 ratchet while preserving existing behavior. No related issue. ## 📋 Changes Summary - Extract German and English pluralization helpers while keeping translation and prefix handling in `pluralize()`. - Remove one early return from `check_for_updates()` by sharing the existing final state update/save path. - Ratchet Ruff `PLR0911` `max-returns` from 12 to 10. - Document the one-lower discovery result: `check_for_updates()` is the next limiter at 10 returns for `max-returns = 9`. - No dependencies, configuration requirements, documentation updates, or generated artifacts are introduced. ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. No documentation changes were needed. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Reorganized pluralization logic and simplified update-checking control flow for improved code maintainability. * **Tests** * Expanded test coverage for pluralization with additional German and English language variations. * **Chores** * Updated code quality configuration standards. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
801ed0e223 |
refactor: remove KleinanzeigenBot re-export from package root (#1150)
## ℹ️ Description - Link to the related issue(s): N/A - Removes the unsupported package-root `KleinanzeigenBot` re-export after tests were moved to their owning modules. - Updates remaining tests and `conftest.py` to import `KleinanzeigenBot` from `kleinanzeigen_bot.app` directly. - Re-enables Ruff's mccabe complexity checks and tightens high Pylint complexity thresholds left over from the old package-root monolith. ## 📋 Changes Summary - Removed `KleinanzeigenBot` from `src/kleinanzeigen_bot/__init__.py`. - Updated all remaining test imports to use `from kleinanzeigen_bot.app import KleinanzeigenBot`. - Enabled `C90`/mccabe complexity linting with an explicit `max-complexity`. - Lowered the broad Pylint limits for branches, locals, returns, statements, and public methods. - Kept the generated/static pydantic error-code mapping explicitly exempted from complexity checks. - No runtime behavior, CLI, config, translations, or ad YAML changes. - No new dependencies. ### ⚙️ 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) - [x] 💥 Breaking change (changes that might break existing user setups, scripts, or configurations) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Reorganized internal module import structure for consistency across the codebase. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3c17f3c8dd |
refactor: move cross-drive fallback test (#1149)
## ℹ️ Description - Link to the related issue(s): Issue # - Move the Windows cross-drive fallback coverage from the package-init test module into the publishing workflow test suite. - This is a behavior-neutral test organization cleanup. ## 📋 Changes Summary - Moved the cross-drive auto-price-reduction path fallback test into `TestAutoPriceReductionDispatch`. - Deleted the now-empty `tests/unit/test_init.py` and its unused local helpers. - No runtime code, CLI behavior, config, translations, or ad YAML changed. ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Reorganized test structure for Windows cross-drive path handling validation to improve maintainability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
48e88fccd3 |
refactor: create app test file and move shell tests (#1148)
## ℹ️ Description - Link to the related issue(s): Issue # - Move app-shell unit tests into a dedicated test module matching `src/kleinanzeigen_bot/app.py`. ## 📋 Changes Summary - Added `tests/unit/test_app.py` for the app-shell test classes and shared fixture. - Kept the cross-drive path fallback coverage in `tests/unit/test_init.py`. - Updated the moved test module to import `KleinanzeigenBot` from `kleinanzeigen_bot.app`. - No runtime, CLI, config, translation, or ad YAML changes. ### ⚙️ Type of Change - [ ] 🐞 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) - Test organization refactor only. ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Added comprehensive unit tests covering command-line subcommands (including defaulting/fallback behavior for ad selector options) and unknown-command handling. * Added assertions for expected initialization behavior and invalid selector validation (including SystemExit code 2). * Refined the unit test modules to simplify imports and improve maintainability, while retaining key cross-drive publish fallback verification. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d0d8e5461b |
refactor: move shipping form tests (#1147)
## ℹ️ Description - Link to the related issue(s): Issue # - Move the shipping option mapping test to the publishing form test module, where the browser-level shipping form behavior is owned. ## 📋 Changes Summary - Moved `test_shipping_options_mapping` from `tests/unit/test_init.py` to `tests/unit/test_publishing_form.py`. - Kept the existing assertions and patch targets intact. - Renamed the remaining `test_init.py` class to describe its cross-drive path fallback coverage. - No runtime behavior, CLI, config, translation, or ad YAML changes. ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Reorganized test coverage for publishing and shipping functionality * Added tests for Windows cross-drive path fallback behavior * Added tests for shipping options mapping verification <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
824ff70e43 |
refactor: move publishing result tests (#1146)
## ℹ️ Description - Link to the related issue(s): N/A - Moves a focused set of publish-ad orchestration/result tests out of the package initialization test module and into the publishing workflow test module. ## 📋 Changes Summary - Move pre-submit timeout retry coverage into `tests/unit/test_publishing_workflow.py`. - Move WANTED shipping delegation coverage into the publishing workflow tests. - Move auto price reduction dispatch coverage into the publishing workflow tests. - Remove now-unused local fixture/imports from `tests/unit/test_init.py`. - No runtime, CLI, config, translation, or ad YAML changes. ### ⚙️ Type of Change - [ ] 🐞 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) - [x] Refactor / test organization only ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Adjusted test coverage for internal functionality validation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
01e764a477 |
refactor: move publish uncertainty tests (#1145)
## ℹ️ Description - Link to the related issue(s): Issue # - Moves publish-ad post-submit uncertainty tests into the publishing workflow test module so `test_init.py` keeps shrinking toward initializer-focused coverage. ## 📋 Changes Summary - Moved the post-submit uncertainty `publish_ad()` test slice into `tests/unit/test_publishing_workflow.py`. - Kept the pre-submit retryability test and unrelated initializer/CLI/login/shipping/ad-management tests in `tests/unit/test_init.py`. - Adjusted test imports only; no runtime behavior, configuration, translation, or YAML changes. ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Reorganized test coverage for publishing workflows, expanding validation of post-submit error handling and confirmation fallback mechanisms to ensure robust publication behavior under various scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f54521b8e6 |
refactor: move publishing diagnostics tests (#1144)
## ℹ️ Description - Link to the related issue(s): N/A - Move the publish failure diagnostics tests out of the initializer-focused unit test module and into the publishing workflow unit test module. - This is a behavior-neutral test organization change. ## 📋 Changes Summary - Moved `TestKleinanzeigenBotDiagnostics` from `tests/unit/test_init.py` to `tests/unit/test_publishing_workflow.py`. - Kept existing assertions and publishing workflow patch targets unchanged. - Removed stale imports from `tests/unit/test_init.py`. - No runtime, CLI, config, translation, or ad YAML changes. ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Enhanced test coverage for diagnostics capture during publishing failures, including verification of log file handling and behavior when diagnostics are disabled. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
2332682d07 |
refactor: move publishing orchestration tests (#1143)
## ℹ️ Description - Link to the related issue(s): Issue #977 - Move another focused set of publishing/update orchestration tests out of `tests/unit/test_init.py` and into `tests/unit/test_publishing_workflow.py`. - This is a behavior-neutral test organization change; no runtime, CLI, config, translation, or ad YAML behavior changes are intended. ## 📋 Changes Summary - Moved display counter progression coverage into the publishing workflow test module. - Moved publish-ads orchestration coverage into a dedicated publishing workflow test class. - Cleaned up imports left behind in `tests/unit/test_init.py`. - Dependencies/configuration/additional requirements: none. ### ⚙️ 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) Test-only refactor; none of the listed runtime change categories apply. ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. Not applicable; test-only relocation. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Improved the ad publishing workflow test coverage by reorganizing publish/cleanup, retry behavior, and “no-retry” scenarios. * Added new async testing utilities (including a reusable mocked page) to better simulate browser interactions. * Expanded regression coverage for processing/counter progression across publishing and updating, including paused ads and “not found” cases. * Removed several redundant or replaced counter/retry/skip tests from the prior initialization test module. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
29aed68706 |
refactor: move publishing workflow tests (#1142)
## ℹ️ Description - Link to the related issue(s): Follow-up to #1141 - Move a publishing/update orchestration test slice into the publishing workflow test module. - Share duplicated test fixtures/helpers that are now used by the relocated publishing workflow tests and existing related tests. - This remains behavior-neutral test organization only. ## 📋 Changes Summary - Added `tests/unit/test_publishing_workflow.py` for publishing workflow orchestration tests. - Moved `TestKleinanzeigenBotUpdateAdsResilience` out of `tests/unit/test_init.py`. - Updated the relocated tests to import `KleinanzeigenBot` directly from `kleinanzeigen_bot.app`. - Moved the duplicated `base_ad_config` fixture into `tests/conftest.py`. - Extracted shared `build_update_ad` and `build_published_ads` test helpers into `tests/conftest.py`. - Removed duplicate fixture/helper definitions from affected unit test files. - No runtime behavior, CLI output, config parsing, translations, or ad YAML handling changes. ### ⚙️ Type of Change - [ ] 🐞 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) - [x] Maintenance/refactor (tests only, no runtime behavior change) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Added comprehensive tests for ad publishing resilience and orchestration, covering retry mechanisms, error handling, timeout scenarios, and failure propagation across different ad states * Reorganized and consolidated test utilities, shared fixtures, and builder helpers across test modules <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f5f3399a66 |
refactor: move app shell out of package init (#1140)
## ℹ️ Description Move the `KleinanzeigenBot` application class out of `__init__.py` into a dedicated `app.py` module. This continues the maintainability-focused refactoring of the package initializer that was started in prior PRs (login flow extraction, publishing workflow extraction, etc.). - Link to the related issue(s): N/A ## 📋 Changes Summary - **New `src/kleinanzeigen_bot/app.py`** (525 lines): contains the full `KleinanzeigenBot` class with all imports, constructor, `run()` command dispatch, `load_ads()`, and delegator methods - **`src/kleinanzeigen_bot/__init__.py`**: reduced from 545 lines to 27 lines — thin package initializer with `colorama` init, `LOG` setup, `KleinanzeigenBot` re-export from `app.py`, `main()` delegator, and `__name__` guard - **Translations**: split `__init__.py` section header in `translations.de.yaml`; class-owned translations moved to new `kleinanzeigen_bot/app.py:` section - **Tests**: updated patch targets from package-root paths to concrete module paths (`kleinanzeigen_bot.UpdateChecker` → `kleinanzeigen_bot.update_checker.UpdateChecker`) ### ⚙️ Type of Change - [ ] 🐞 Bug fix - [ ] ✨ New feature - [ ] 💥 Breaking change - [x] 🔧 Refactoring (no user-facing behavior changes) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Centralized runtime command handling in a new application entrypoint class while keeping the same CLI-style command set. * Slimmed the package initializer to re-export the main bot class and delegate CLI execution through `main(args)`. * **Bug Fixes** * Improved Windows console compatibility during startup. * **Tests** * Updated unit tests and mocks to reflect the new location of command-handling logic and related wiring. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
35bd393dc4 |
refactor: extract login/auth flow into login_flow.py (#1139)
## ℹ️ Description Extract all login, Auth0 form-fill, captcha, SMS/email verification, GDPR, login-state detection, and diagnostics-capture logic from `__init__.py` into a new `login_flow.py` module. ## 📋 Changes Summary - Create `login_flow.py` (~634 lines) with module-level async functions accepting explicit `web: WebScrapingMixin` + keyword arguments - Thin delegators in `__init__.py` bind `KleinanzeigenBot` instance attributes to module calls - Remove 12 dead delegator methods; tests call module functions directly - Move `LoginDetectionResult`/`LoginDetectionReason` types to `login_flow` - Move `TestKleinanzeigenBotAuthentication` to `tests/unit/test_login_flow.py`, update patch targets - Move login translations to `login_flow.py` section in `translations.de.yaml` - Fix diagnostics guard reset per login attempt - Remove `is_logged_in_web()`; pass username explicitly - Make cross-module functions public: `login_flow` internal helpers and `WebScrapingMixin` helpers (`dismiss_consent_banner`, `effective_timeout`, `extract_visible_text`) ### ⚙️ Type of Change - [x] ✨ New feature (adds new functionality without breaking existing usage) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Refactor** * Reworked the authentication/login process to use a dedicated login flow, improving handling of multi-step sign-in and related verification prompts. * Standardized login-status detection and diagnostics behavior when login state can’t be determined. * Updated text-extraction and timeout behavior for DOM-based fallbacks, and aligned consent-banner dismissal to the public API. * **Tests** * Added extensive unit tests for the login flow and login-state detection. * Updated existing tests to reflect the refactored helpers and consent-banner method naming. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ecd3ea35cf |
refactor: extract publishing workflow (#1137)
## ℹ️ Description - Link to the related issue(s): N/A - Extracts publish/update orchestration from the monolithic bot class into `publishing_workflow.py` as Step 21 of the monolith breakdown. ## 📋 Changes Summary - Added `src/kleinanzeigen_bot/publishing_workflow.py` for publish/update orchestration, retry/uncertainty handling, result checks, and delete-before/after-publish wiring. - Kept `KleinanzeigenBot` publish/update methods as narrow delegation seams with explicit dependencies. - Moved WANTED ad type selection into `publishing_form.fill_ad_form()` so form details stay in the form module. - Updated German translations and unit tests to patch concrete workflow module paths. - No dependency or configuration changes. ### ⚙️ Type of Change - [ ] 🐞 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) - [x] Refactor (no user-facing behavior change) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved form rendering for WANTED ad type by ensuring the ad type is pre-selected before populating other form fields. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
eae9dc9fc1 |
refactor: extract publishing form orchestrator (#1136)
## ℹ️ Description Move the last form-filling remnant out of `__init__.py` into `publishing_form.py`, where all individual form section functions already live. - `_fill_ad_form()` only delegated to the already-extracted section functions (`set_category`, `set_special_attributes`, `set_shipping_form`, `set_pricing_fields`, `set_contact_fields`, `fill_image_section`). - Replaced with a public module-level `publishing_form.fill_ad_form()` that accepts explicit `web`, `root_url`, `ad_defaults`, `ad_file`, `ad_cfg`, and `mode` parameters. - `publish_ad()` now calls `_publishing_form.fill_ad_form(self, ...)` instead of `self._fill_ad_form(...)`. ## 📋 Changes Summary - Extracted `fill_ad_form()` into `publishing_form.py` as a public module-level async function - Removed `_fill_ad_form()` method from `KleinanzeigenBot` class - Updated call site in `publish_ad()` to pass explicit parameters - Updated test patch target in `test_publishing_persistence.py` and docstring in `test_init.py` ### ⚙️ Type of Change - [ ] 🐞 Bug fix - [ ] ✨ New feature - [ ] 💥 Breaking change ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Reorganized ad form-filling logic for improved maintainability and code structure. * **Tests** * Updated unit tests to reflect internal code reorganization. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
154c37b26e |
refactor: extract special-attributes/condition form section (#1135)
## ℹ️ Description Extracts special-attribute and condition form logic from `__init__.py` into `publishing_form.py` to continue module decomposition. ## 📋 Changes Summary - **`publishing_form.py`**: Added `set_special_attributes`, `_set_condition`, and helpers (`_pick_special_attribute_candidate`, `_special_attribute_candidate_priority`, `_describe_special_attribute_candidate`) adapted as module-level functions taking `web: WebScrapingMixin` - **`__init__.py`**: Removed the moved functions, cleaned up stale imports (`re`, `_ad_form_helpers`, `_select_button_combobox`, `ensure`, `Element`), updated `_fill_ad_form` to delegate via `_publishing_form.set_special_attributes()` - **`translations.de.yaml`**: Moved `_set_condition` and `set_special_attributes` sections from `__init__.py:` → `publishing_form.py:` block - **`test_init.py`**: Moved special-attribute and condition tests to `test_publishing_form.py`, updated orchestration patch targets - **`test_publishing_form.py`**: Added `TestSpecialAttributes`, `TestConditionSelector`, and `TestConditionFallbackToGenericHandler` classes with adapted imports and patches ### ⚙️ Type of Change - [x] Refactor — no behavior changes, internal module reorganization only ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. |
||
|
|
875b8b063a |
refactor: extract publishing shipping form section (#1134)
## ℹ️ Description - Extracts the publishing shipping form workflow from `KleinanzeigenBot` into `publishing_form.py`. - Keeps runtime behavior, selectors, waits, messages, and browser interactions unchanged while moving translation ownership. ## 📋 Changes Summary - Move WANTED shipping handling, shipping radio/dialog handling, shipping option toggling, and the private React-fiber button-combobox helper into `publishing_form.py`. - Update `__init__.py` to delegate shipping form work and keep only orchestration/seam responsibilities. - Move direct shipping behavior tests to `tests/unit/test_publishing_form.py`; keep a shipping orchestration seam test in `tests/unit/test_init.py`. - Move German translation entries for shipping messages to the `publishing_form.py` translation section. ### ⚙️ Type of Change - [ ] 🐞 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 - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary of changes * **Refactor** * Improved publishing ad shipping form handling by routing wanted vs non-wanted ads to the correct UI flow. * Enhanced Versand (shipping) combobox selection for wanted ads to ensure the right option is chosen. * Strengthened the shipping dialog flow for pickup vs shipping-with-options, including more reliable shipping price confirmation and retry behavior. * **Tests** * Expanded unit tests covering shipping dialog interactions, carrier/option selection, wanted-shipping combobox behavior, and timeout/error scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e9ed698dce |
refactor: extract publishing pricing form section (#1133)
## ℹ️ Description - Link to the related issue(s): Issue # - Extracts the publishing pricing/direct-buy/description form section from the main bot class as the next monolith-breakdown step. ## 📋 Changes Summary - Added publishing_form.set_pricing_fields() for price type/amount, sell-directly/direct-buy radios, and description filling. - Replaced the inline _fill_ad_form() pricing block with the new publishing form section call. - Moved direct unit coverage for pricing/direct-buy/description behavior into test_publishing_form.py and kept orchestration seam tests in test_init.py. - Moved existing German translation ownership entries to the new function path without wording changes. - No dependencies, configuration changes, CLI changes, schema changes, or generated artifacts. ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project standards. - [x] I have tested my changes and ensured that all tests pass (pdm run test). - [x] I have formatted the code (pdm run format). - [x] I have verified that linting passes (pdm run lint). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Refactor** * Centralized pricing-related ad form filling (price type, price amount, ad description with affixes, and direct-buy/buy-now controls) into a shared publishing helper for more consistent behavior. * **Bug Fixes** * Improved handling of pricing/buy-now selection failures by surfacing clearer, localized timeout errors. * **Tests** * Added dedicated coverage for pricing field interactions, including dropdown skipping when not applicable, correct amount input behavior, and description generation usage. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
137a7691a7 |
refactor: extract publishing image form section (#1132)
## ℹ️ Description - Link to the related issue(s): N/A - Extracts the publishing image cleanup and upload section from the bot monolith into `publishing_form.py` as Step 16 of the monolith breakdown plan. ## 📋 Changes Summary - Move image cleanup/upload behavior into `fill_image_section()` and `upload_images()`. - Replace the monolith image block with a publishing form seam call. - Move direct image behavior tests from `test_init.py` to `test_publishing_form.py`. - Update German translation ownership paths without changing translated text. - No dependency, configuration, CLI, schema, or generated artifact changes. ### ⚙️ 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) - [x] ♻️ Refactor (no intended behavior change) ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Image upload handling has been reorganized and consolidated in the publishing module for improved code organization. The image processing workflow now includes enhanced timeout management and marker-based completion detection to ensure all images are reliably processed during ad publication. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9d146c2b96 |
refactor: extract publishing contact form section (#1131)
## ℹ️ Description - Link to the related issue(s): Issue # - Refactors the publishing form code by moving the contact/location form section out of the package initializer into the publishing form module. ## 📋 Changes Summary - Move contact field handling, contact location handling, and city combobox helpers into publishing_form.py. - Keep the publishing form orchestration delegating through an explicit web/contact boundary. - Update unit tests to patch and call the new publishing form functions. - Move German translation ownership paths for the relocated messages without changing translated text. - No dependencies, configuration changes, or additional requirements introduced. ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (pdm run test). - [x] I have formatted the code (pdm run format). - [x] I have verified that linting passes (pdm run lint). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Updated ad publishing to populate contact and location fields via dedicated public publishing-form helpers, with revised import wiring for ad model types. * Removed older internal city/contact handling logic and replaced it with clearer helper-driven flow. * **Bug Fixes** * Improved city/location hardening: safer city extraction, combobox-style selection with ambiguity checks, timeout-based fallbacks, and correct handling for read-only city inputs. * More resilient contact field population (zipcode/location, street, name, phone), with graceful handling when optional elements are missing. * **Tests** * Added/updated unit tests to cover city/contact hardening and category probe/picker selection behaviors, asserting helper calls rather than private methods. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f02816cb06 |
refactor: extract publishing category form section (#1130)
## ℹ️ Description - Link to the related issue(s): N/A - Extracts the publishing category form section from the monolithic bot class into a focused publishing form module while preserving category selection and suggestion fallback behavior. ## 📋 Changes Summary - Move category selection and category suggestion fallback into `src/kleinanzeigen_bot/publishing_form.py`. - Update publish form orchestration to call the extracted category function through a narrow web/root_url boundary. - Update unit tests and German translation ownership for the new module path. - No dependencies, configuration changes, or additional requirements introduced. ### ⚙️ Type of Change - [ ] 🐞 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 - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Restructured category selection logic for improved maintainability and code organization. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
dd6a1955ef |
refactor: extract publishing form helpers (#1129)
## ℹ️ Description - Extract browser-independent publishing form helpers from the monolith into `ad_form_helpers.py`. - Prepares later publishing form extraction without changing browser behavior. ## 📋 Changes Summary - Move the Versand combobox selector constant to `ad_form_helpers.py`. - Move location matching logic to `ad_form_helpers.py`. - Update production call sites and helper ownership tests. - Keep form sections and browser-dependent logic in `__init__.py`. ### ⚙️ Type of Change - [ ] 🐞 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 - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved internal code organization by consolidating shared helpers for form-related utilities and location matching logic. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
fda56b109c |
refactor: prepare web helpers for publishing form extraction (#1128)
## ℹ️ Description - Link to the related issue(s): Issue #930 - Moves a shared consent-banner browser helper onto the generic web scraping mixin so later publishing form refactors can use it without depending on the main bot class. ## 📋 Changes Summary - Move `_dismiss_consent_banner()` from `KleinanzeigenBot` to `WebScrapingMixin` without changing selector, timeout, click, or sleep behavior. - Move the helper's focused unit tests from `test_init.py` to `test_web_scraping_mixin.py`. - No dependencies, configuration changes, or additional requirements introduced. ### ⚙️ 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) None of the listed categories apply; this is an internal behavior-equivalent refactor. ## ✅ Checklist Before requesting a review, confirm the following: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Refactor** * Moved consent-banner dismissal logic into a shared web-scraping helper, and updated the bot flow to proceed directly after SMS verification. * **Tests** * Removed unit tests that covered the old bot-level consent-banner handling. * Added new async unit tests to verify the shared helper clicks the consent accept button when present and does nothing when absent. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
816cf3ed9e |
refactor: rename publishing persistence module (#1127)
## ℹ️ Description - Link to the related issue(s): Issue # - Rename the persistence-only publishing module so the module name matches its current responsibility. ## 📋 Changes Summary - Rename `publishing_flow.py` to `publishing_persistence.py`. - Update production imports/calls and test patch paths. - Move persistence tests to `test_publishing_persistence.py` and keep submission tests under `test_publishing_submission.py`. - Update the German translation section header for the renamed module. ### ⚙️ Type of Change - [ ] 🐞 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 - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved the way published ad changes are saved after submitting or updating, ensuring related fields (such as image updates and pricing adjustments) are correctly retained. * **Bug Fixes** * Publishing now continues even if the follow-up save step fails, preventing unnecessary interruptions. * **Tests** * Updated existing tests to match the revised persistence behavior. * Added new unit tests covering persistence outcomes, logging, rollback behavior, and image/price update handling. * Simplified the publishing submission test suite setup. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5c22795728 |
fix: handle shipping select for pro accounts (#1126)
## ℹ️ Description - Link to the related issue(s): Issue #1125 - Fixes PRO/commercial account publishing where the Versand field is rendered as a custom `button[role="combobox"]` dropdown by the PostListingForm Astro island, not as a native `versand_s` `<select>` and not as the private-account radio-button UI. - Uses the confirmed PRO DOM model from the issue comments/screenshots: category-prefixed IDs such as `kunst.versand` / `uhren.versand`, placeholder `Bitte wählen`, and options `Versand möglich` (`ja`) / `Nur Abholung` (`nein`). ## 📋 Changes Summary - Detect the custom Versand combobox before falling back to the private radio/dialog shipping flow. - Locate the combobox by the confirmed category-prefixed id shape and by the selected-option label relationship (`aria-labelledby$="versand-selected-option"`) so the lookup is not tied to one category prefix. - Select `Versand möglich` for `shipping_type: SHIPPING` and `Nur Abholung` for `shipping_type: PICKUP` by visible option text via `web_select_button_combobox()`. - Apply the same safer Versand combobox selector to the existing WANTED-ad shipping combobox path. - Keep the private/non-commercial radio-button and shipping-options-dialog path as the fallback when the custom combobox is absent. - Add regression coverage for the PRO combobox path and relax existing shipping-flow assertions so they check behavior instead of exact probe ordering/timeout plumbing. - Add the German translation for the combobox error message. ### ⚙️ Type of Change - [x] 🐞 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 - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Improved the WANTED shipping flow to detect and use native “Versand” dropdown controls when available, selecting the correct shipping option directly. * **Bug Fixes** * Added clearer errors for unsupported shipping types and standardized timeout behavior when native selection fails; continues to fall back to the existing radio/dialog approach when the control is missing. * **Tests** * Expanded unit tests to cover the native Versand combobox route, including selector coverage and updated probe/call-order expectations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f94567a983 |
fix: use canonical title for owned ad downloads (#1123)
## ℹ️ Description - Link to the related issue(s): Issue #1119 - Fixes owned-ad downloads where the public/detail page title may include UI status decoration that makes the title exceed the semantic 65-character title limit. ## 📋 Changes Summary - Resolve download titles once via manage-ads metadata when available. - Reuse the resolved title for folder naming, file stems, and persisted ad config data. - Keep manual page title extraction as the fallback without stripping or truncation. - Add regression coverage for canonical metadata titles and fallback extraction. ### ⚙️ Type of Change - [x] 🐞 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (pdm run test). - [x] I have formatted the code (pdm run format). - [x] I have verified that linting passes (pdm run lint). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Enhanced ad title resolution to prioritize cached published metadata when available, with fallback to page extraction for improved reliability. * Improved directory handling to consistently use the resolved ad title for naming and saved info templates. * **Bug Fixes** * Corrected title/metadata extraction flow to avoid redundant title reads and ensure creation dates are reliably sourced from the expected page elements. * **Tests** * Updated and added unit test coverage for title resolution, directory behaviors, and extraction call ordering. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
439c36e851 |
fix: click Auth0 Weiter after captcha (#1120) (#1122)
## ℹ️ Description Fixes issue #1120 by handling the Auth0 captcha branch after email submit. When captcha appears, the bot now probes for a visible/clickable "Weiter" button and clicks it before waiting for the password page. The normal no-captcha login flow remains unchanged. - Link to the related issue(s): Issue #1120 - Describe the motivation and context for this change. ## 📋 Changes Summary - Added a captcha-only Auth0 identifier handler that waits for captcha completion, probes for "Weiter" with `web_probe`, and clicks it if present. - Kept the existing no-captcha login path unchanged. - Updated German translations for the new log messages. - Adjusted unit tests for the new login helper. ### ⚙️ Type of Change - [x] 🐞 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 - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. No additional docs changes were needed beyond translations. By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved login robustness to detect optional reCAPTCHA on the email/identifier step; users are notified when a captcha appears, can provide input, and the login resumes once advanced. * **Tests** * Updated unit tests to cover and control the captcha-handling behavior during the login flow, including the missing-password-step scenario. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5d5c25d753 |
refactor: extract publishing submission flow (#1121)
## ℹ️ Description *Refactor the publishing submission/confirmation flow into a dedicated module while keeping behavior identical.* - Link to the related issue(s): N/A - Describe the motivation and context for this change: Reduce coupling in the publishing workflow and keep the persistence boundary separate. ## 📋 Changes Summary - Added `src/kleinanzeigen_bot/publishing_submission.py` for captcha, submit, confirmation, and ad-id recovery. - Removed the submission/confirmation helpers from `publishing_flow.py`, leaving persistence helpers there. - Updated the `publish_ad()` call site and tests to use the new concrete module path. - Moved the translated log-message entries for the extracted submission flow. ### ⚙️ 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: - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] 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. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Ad submission and confirmation logic moved into a dedicated submission module for clearer separation. * **Bug Fixes** * Improved publish confirmation: better captcha handling, post-submit dialogs (upsell/payment, imprint guidance, “no image”) and avoidance of stale-referrer fallbacks. * Now fails loudly and avoids persisting when ad confirmation is uncertain. * **Tests** * Unit tests updated to target the new submission flow and recovery/persistence behaviors. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
97256c1d70 |
fix: apply after_delete policy when title-mode delete returns all-404 with id=None (#1116)
## ℹ️ Description When `delete_old_ads_by_title=True` and `ad_cfg.id=None` and all remote DELETE requests return 404, the `after_delete` policy (DISABLE/RESET) was silently skipped because the orchestrator's `id_before`/`id_after` heuristic could not distinguish "not attempted" from "attempted but all 404." This PR replaces the heuristic with an explicit `DeleteResult` NamedTuple returned from `delete_ad()`, communicating whether HTTP DELETE requests were actually sent (`attempted`) separately from whether any returned 200 (`deleted`). Closes #1103. ## 📋 Changes Summary ### 🐞 Bug fix - **`delete_flow.py`** — New `DeleteResult(deleted, attempted)` NamedTuple replaces the `bool` return from `delete_ad()`. The orchestrator now uses `result.attempted` directly to decide whether to apply `after_delete` policy. - Removed the fragile `id_before`/`ad_cfg.id`-based heuristic entirely. ### 🧪 Tests - Updated all 9 existing `delete_ad()` tests for the new tuple return type - Updated 3 orchestrator tests (`delete_ads`) for the new `DeleteResult` API - Added regression test `test_cleanup_on_title_match_all_404_with_id_none` — `ad_cfg.id=None`, title-match, all responses 404, `after_delete=RESET` is applied ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved deletion tracking to distinguish attempted vs successful deletions, ensuring cleanup policies run correctly across edge cases. * **Tests** * Updated unit tests to reflect the new deletion outcomes and added a regression test to verify cleanup behavior when title-mode deletions all fail. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d2b944568c |
fix: close submit uncertainty gap, surface silent TOCTOU race, and translate remaining TimeoutError messages (#1114)
## ℹ️ Description Three bug fixes affecting browser automation correctness and i18n completeness: - **#1110**: Move upsell dialog dismiss inside the post-submit try/except block so timeouts during upsell interaction are treated as uncertain submission rather than retryable errors, preventing duplicate listings. - **#1112**: Change `web_set_input_value` to detect TOCTOU races (element disappearing between `web_find` and JS execution). The JS now returns `false` on missing element, and the Python side raises `TimeoutError` so callers see a clear failure instead of proceeding with stale field data. **Approach**: sentinel return (not JS throw) because nodriver returns `ExceptionDetails` silently rather than raising Python exceptions. - Translate two remaining untranslated `TimeoutError` messages in `__init__.py:_set_condition` and `web_scraping_mixin.py:_run_with_timeout_retries`. ## 📋 Changes Summary | File | Change | |---|---| | `publishing_flow.py` | Move upsell dismiss inside `try` block (#1110) | | `web_scraping_mixin.py` | Sentinel + `TimeoutError` for TOCTOU race (#1112); translate guard-clause message | | `__init__.py` | Translate `No condition radio matched values` message | | `translations.de.yaml` | Add German translations for all 3 new messages | ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved and localized error messages for timeouts and form interactions. * Better handling of element race conditions to reduce submission failures. * **Refactor** * Reworked ad submission confirmation flow to unify exception handling and make upsell dismissal more robust. * **Tests** * Added tests covering input-setting behavior and TOCTOU race handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ce3c1adf89 |
fix: correct republication timing and content hash computation (#1113)
## ℹ️ Description Two small bug fixes affecting ad lifecycle correctness, both in `ad_loading.py`. - Fix off-by-one republication comparison (#1099): ads are now eligible for republication exactly when their age reaches the configured interval, instead of one day late. - Fix content hash divergence (#1100): `update-content-hash` now computes hashes from the raw ad config (matching `check_ad_changed`), preventing spurious "ad changed" detection on every publish. ## 📋 Changes Summary ### 🐞 Bug fixes 1. **#1099** — `check_ad_republication`: changed `<=` to `<` so ads are due at `ad_age.days == republication_interval` rather than one day later. 2. **#1100** — `update_content_hashes`: switched from `ad_cfg.update_content_hash()` (hydrated Ad model) to `AdPartial.model_validate(ad_cfg_orig).update_content_hash()` (raw config dict), matching `check_ad_changed`. Also uses `.get()` to handle legacy ad files without a `content_hash` key. ### 🧪 Tests Updated `test_counter_progression` to pre-compute hashes using the same `AdPartial` path as production code. ### ⚙️ Type of Change - [x] 🐞 Bug fix (non-breaking change which fixes an issue) ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Adjusted ad republication timing logic—ads at the republication interval boundary are now correctly treated as not yet due for republication. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
2971d650c7 |
refactor: extract captcha flow and submit/confirm to dedicated modules (#1111)
## ℹ️ Description Extract captcha detection and ad submit/confirmation logic from `__init__.py` into focused modules. This is part of an ongoing effort to decompose the monolithic `KleinanzeigenBot` class into maintainable, testable module functions with explicit dependencies. - Related to #1110 (pre-existing submit uncertainty boundary gap, tracked separately) ## 📋 Changes Summary - **New module `captcha_flow.py`**: `check_and_wait_for_captcha()` shared by login and publish flows. Captcha auto-restart raises `CaptchaEncountered` on non-login pages. - **`publishing_flow.py` gains `submit_and_confirm_ad()`**: submit click, upsell dismissal, imprint guidance, no-image confirmation, payment form detection, confirmation URL polling with tracking-data fallback, ad ID extraction. Plus private `_try_recover_ad_id_from_redirect()`. - **`web_set_input_value()` promoted to `WebScrapingMixin`**: generic browser primitive for setting DOM values with React-compatible events. Removed from bot class. - **Translations moved** to match new module/key locations. - **Coverage**: `captcha_flow.py` 100%, `publishing_flow.py` 90.78% (up from 83.98%). - **Cleanup**: removals from `__init__.py` (233 lines), stale/low-value tests removed from `test_init.py`. No CLI, config, YAML, or runtime behavior changes. ### ⚙️ Type of Change - [x] ✨ New feature (adds new functionality without breaking existing usage) — technically a refactor; no behavioral changes ## ✅ Checklist - [x] I have reviewed my changes to ensure they meet the project's standards. - [x] I have tested my changes and ensured that all tests pass (`pdm run test`). - [x] I have formatted the code (`pdm run format`). - [x] I have verified that linting passes (`pdm run lint`). - [x] I have updated documentation where necessary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * More reliable ad publishing with a fallback recovery when confirmation fails * Improved shipping-price entry with read-back verification and automatic retries * More consistent form field handling (contact info, price, description) and improved captcha prompting during login * **Refactor** * Internal publishing and captcha flows reorganized for more robust user interactions * **Tests** * Added extensive tests for captcha handling and ad submission/confirmation flows <!-- end of auto-generated comment: release notes by coderabbit.ai --> |