Commit Graph

1012 Commits

Author SHA1 Message Date
Jens
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 -->
2026-06-24 10:36:42 +02:00
Jens
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 -->
2026-06-22 21:37:17 +02:00
Jens
dedf4cb4f0 refactor: extract template name renderer from AdExtractor (#1171)
## ℹ️ Description

Extract the priority-based template rendering algorithm from
`_render_download_name_with_budget` into a module-level pure function
`_render_name_with_budget`. The method becomes a thin wrapper that
delegates rendering and handles truncation warnings.

## 📋 Changes Summary

- Added `_render_name_with_budget(template, ad_id, title, max_length) ->
tuple[str, bool, bool]` as a module-level pure function with no
AdExtractor dependency
- Rewrote `_render_download_name_with_budget` as a thin wrapper that
calls the pure function and handles logging
- Removed `_reserved_for_pending_placeholders` static method (logic
inlined in the pure function)

### ⚙️ 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.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Improved download name handling with enhanced truncation logic for
improved filename generation.
* Added warnings when download names are automatically shortened due to
length constraints.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-22 20:57:25 +02:00
dependabot[bot]
e15c57f131 ci(deps): bump actions/checkout from 6.0.3 to 7.0.0 in the all-actions group (#1170)
Bumps the all-actions group with 1 update:
[actions/checkout](https://github.com/actions/checkout).

Updates `actions/checkout` from 6.0.3 to 7.0.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v7.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>block checking out fork pr for pull_request_target and workflow_run
by <a href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li>
<li>Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the
minor-actions-dependencies group across 1 directory by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/checkout/pull/2458">actions/checkout#2458</a></li>
<li>Bump flatted from 3.3.1 to 3.4.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/checkout/pull/2460">actions/checkout#2460</a></li>
<li>Bump js-yaml from 4.1.0 to 4.2.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/checkout/pull/2461">actions/checkout#2461</a></li>
<li>Bump <code>@​actions/core</code> and
<code>@​actions/tool-cache</code> and Remove uuid by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/checkout/pull/2459">actions/checkout#2459</a></li>
<li>upgrade module to esm and update dependencies by <a
href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2463">actions/checkout#2463</a></li>
<li>Bump the minor-npm-dependencies group across 1 directory with 3
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/actions/checkout/pull/2462">actions/checkout#2462</a></li>
<li>getting ready for checkout v7 release by <a
href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2464">actions/checkout#2464</a></li>
<li>update error wording by <a
href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2467">actions/checkout#2467</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/aiqiaoy"><code>@​aiqiaoy</code></a> made
their first contribution in <a
href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v6.0.3...v7.0.0">https://github.com/actions/checkout/compare/v6.0.3...v7.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9c091bb21b"><code>9c091bb</code></a>
update error wording (<a
href="https://redirect.github.com/actions/checkout/issues/2467">#2467</a>)</li>
<li><a
href="1044a6dea9"><code>1044a6d</code></a>
getting ready for checkout v7 release (<a
href="https://redirect.github.com/actions/checkout/issues/2464">#2464</a>)</li>
<li><a
href="f0282184c7"><code>f028218</code></a>
Bump the minor-npm-dependencies group across 1 directory with 3 updates
(<a
href="https://redirect.github.com/actions/checkout/issues/2462">#2462</a>)</li>
<li><a
href="d914b262ff"><code>d914b26</code></a>
upgrade module to esm and update dependencies (<a
href="https://redirect.github.com/actions/checkout/issues/2463">#2463</a>)</li>
<li><a
href="537c7ef99c"><code>537c7ef</code></a>
Bump <code>@​actions/core</code> and <code>@​actions/tool-cache</code>
and Remove uuid (<a
href="https://redirect.github.com/actions/checkout/issues/2459">#2459</a>)</li>
<li><a
href="130a169078"><code>130a169</code></a>
Bump js-yaml from 4.1.0 to 4.2.0 (<a
href="https://redirect.github.com/actions/checkout/issues/2461">#2461</a>)</li>
<li><a
href="7d09575332"><code>7d09575</code></a>
Bump flatted from 3.3.1 to 3.4.2 (<a
href="https://redirect.github.com/actions/checkout/issues/2460">#2460</a>)</li>
<li><a
href="0f9f3aa320"><code>0f9f3aa</code></a>
Bump actions/publish-immutable-action (<a
href="https://redirect.github.com/actions/checkout/issues/2458">#2458</a>)</li>
<li><a
href="f9e715a95f"><code>f9e715a</code></a>
block checking out fork pr for pull_request_target and workflow_run (<a
href="https://redirect.github.com/actions/checkout/issues/2454">#2454</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/checkout/compare/v6.0.3...v7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=6.0.3&new-version=7.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 16:19:09 +02:00
Jens
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 -->
2026-06-22 16:07:04 +02:00
Jens
388d0fb3c8 refactor: separate download ad persistence concerns (#1168)
## ℹ️ Description

Separate downloaded-ad persistence concerns from `download_ad()` in
`extract.py`. The original method mixed browser extraction,
local-settings preservation, and a filesystem transaction — three
independently meaningful and operationally sensitive concerns.

## 📋 Changes Summary

- **Extracted `_preserve_local_settings_from_existing_ad()`** —
preserves local-only settings (repost_count, republication_interval,
auto_price_reduction, price_reduction_count) from an existing ad YAML
into the new config. No-op unless `preserve_local_settings` is enabled.
- **Extracted `_commit_staged_download()`** — owns the full filesystem
transaction: YAML save, backup collision check, rename
final→backup→staging→final, backup cleanup, and rollback on failure.
- **Simplified `download_ad()`** — now a clean 3-step orchestrator:
extract → preserve → commit.
- **Restructured translation keys** in `translations.de.yaml` to follow
the new caller function names.

No behavior changes to preservation, rollback, YAML handling, or
exception propagation.

### ⚙️ 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**
* Enhanced ad download process with improved backup and rollback
mechanisms for better reliability
* Refined handling of local settings preservation during re-downloads to
maintain data integrity

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-22 15:31:13 +02:00
Jens
eeeae6ea5e refactor: extract shipping helpers from set_shipping (#1167)
## ℹ️ Description

Refactor `set_shipping` in `publishing_form.py` by extracting four
distinct concerns into focused private helpers. Simplifies a
~55-statement function into a ~20-statement orchestrator with a flat
if-return chain.

## 📋 Changes Summary

- Extract `_select_shipping_combobox_if_present` — PRO/commercial
combobox handling
- Extract `_set_pickup_shipping` — PICKUP radio + fieldset fallback
- Extract `_set_configured_shipping_options` — shipping options dialog +
MODIFY nav
- Extract `_enable_platform_default_shipping` — enable shipping toggle
- Keep legacy `shipping_costs` guard inline in `set_shipping` (per
review)
- Re-key translation entries under new caller-function names (duplicate
"Failed to set shipping..." under both combobox and pickup helpers)
- `set_shipping` slimmed from ~55 → ~20 ruff-counted statements

### ⚙️ 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
* **Shipping configuration**: Refactored the shipping configuration
logic to improve internal code structure and maintainability, enabling
more reliable handling of shipping options across different scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-22 13:16:37 +02:00
Jens
780743aee2 refactor: separate download_ads workflow modes (#1166)
## ℹ️ Description

Extract four private async helpers from `download_ads()` to separate
per-mode workflow logic.

- Motivation: Reduce function complexity, enable tighter pylint
thresholds

## 📋 Changes Summary

- Extracted `_fetch_published_ads_by_id` — fetch and build published ads
lookup dict
- Extracted `_download_all_ads` — download all ads from overview page
- Extracted `_download_new_ads` — download only unsaved ads (preserving
URL dedup behavior)
- Extracted `_download_ads_by_ids` — download specific ads by numeric ID
(preserving distinct warning semantics)
- Slimmed `download_ads()` to ~48-line orchestrator (was ~124 lines)
- Restructured `translations.de.yaml` — messages moved to per-function
sections
- Ratcheted pylint thresholds: max-statements 90→76, max-branches 35→31

### ⚙️ Type of Change
- [ ] 🐞 Bug fix
- [ ]  New feature
- [ ] 💥 Breaking change

Internal refactor, no user-visible behavior 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`): 1286 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.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Strengthened code quality standards and refactored internal ad
downloading logic for improved maintainability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-22 11:28:23 +02:00
Jens
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 -->
2026-06-22 07:12:23 +02:00
Jens
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 -->
2026-06-21 22:39:36 +02:00
Alex Strutsysnkyi
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 -->
2026-06-21 20:16:52 +02:00
kleinanzeigen-bot-tu[bot]
0c75eea071 chore: Update Python dependencies (#1163)
✔ Update wrapt 2.2.1 -> 2.2.2 successful
  ✔ Update coverage 7.14.1 -> 7.14.2 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-21 19:37:22 +02:00
Jens
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 -->
2026-06-21 19:36:56 +02:00
Jens
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 -->
2026-06-21 09:26:52 +02:00
Jens
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 -->
2026-06-21 08:11:02 +02:00
Jens
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 -->
2026-06-20 22:22:21 +02:00
Jens
0b4b9eb397 refactor: split browser session setup (#1156)
## ℹ️ Description

- Link to the related issue(s): N/A
- Split browser session setup into smaller, behavior-preserving helpers
so browser startup orchestration is easier to review and maintain.

## 📋 Changes Summary

- Extract remote debugging connection handling from browser session
creation.
- Extract launch argument, user-data-dir, Nodriver config, profile
preparation, and extension setup helpers.
- Add matching German translation entries for log messages moved into
helper functions.
- Leave lint thresholds unchanged because the diagnostics follow-up
remains separate.

### ⚙️ 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

## Summary by CodeRabbit

* **Refactor**
* Improved browser session setup by separating remote-debugging
connections from fresh browser startup.
* Hardened launch argument construction and user data directory
resolution for more consistent configuration.
* Improved browser startup failure cleanup to ensure resources are
released reliably.
* Kept troubleshooting output behavior, including better guidance for
debugging scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-20 14:54:15 +02:00
Jens
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 -->
2026-06-20 12:33:06 +02:00
Jens
75f34775e2 refactor: reduce return-count complexity (#1154)
## ℹ️ Description

- Link to the related issue(s): Issue #
- Reduces return-count complexity in the update checker while preserving
existing update-check behavior.

## 📋 Changes Summary

- Extracted GitHub release selection and commit-ish validation from
`check_for_updates` into a private helper.
- Moved German translation keys for logger calls that now originate from
the helper.
- Ratcheted Ruff/Pylint `max-returns` from 10 to 8.

### ⚙️ 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

* **Bug Fixes**
* The update checker now correctly handles the latest update channel by
excluding pre-release versions and recommending only stable releases.

* **Chores**
  * Code quality configuration settings were adjusted.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-20 10:49:22 +02:00
Jens
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 -->
2026-06-19 23:15:27 +02:00
kleinanzeigen-bot-tu[bot]
8b0e4f468d chore: Update Python dependencies (#1151)
✔ Update pytest 9.1.0 -> 9.1.1 successful
  ✔ Update msgpack 1.2.0 -> 1.2.1 successful
  ✔ Update ruff 0.15.17 -> 0.15.18 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-19 16:34:44 +02:00
Jens
e8752ce10e refactor: reduce price reduction complexity (#1152)
## ℹ️ Description

- Link to the related issue(s): Issue #
- Extracts the first complexity hotspot in the ratcheting sequence by
separating the final auto price-reduction application flow from
`apply_auto_price_reduction`.
- This keeps behavior unchanged while allowing the Ruff `max-returns`
transitional limit to move from 13 to 12.

## 📋 Changes Summary

- Extracted `_apply_price_reduction_decision` for the final
price-reduction mutation/logging phase.
- Moved German translations for the relocated `LOG.info` calls to the
new helper function key.
- Lowered `tool.ruff.lint.pylint.max-returns` from 13 to 12.
- No dependency or runtime configuration changes 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)

Refactor-only change; none of the listed user-facing change types 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.

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**
  * Updated linting configuration thresholds for code quality standards.

* **Refactor**
  * Improved internal code organization for better maintainability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-19 16:19:33 +02:00
Jens
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 -->
2026-06-19 15:00:45 +02:00
Jens
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 -->
2026-06-19 14:29:57 +02:00
Jens
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 -->
2026-06-19 13:54:14 +02:00
Jens
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 -->
2026-06-19 13:00:36 +02:00
Jens
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 -->
2026-06-19 12:25:01 +02:00
Jens
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 -->
2026-06-19 11:40:26 +02:00
Jens
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 -->
2026-06-19 11:14:37 +02:00
Jens
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 -->
2026-06-19 10:40:02 +02:00
Jens
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 -->
2026-06-19 09:35:36 +02:00
Jens
ee581e383f refactor: clean up source hygiene (#1141)
## ℹ️ Description

- Link to the related issue(s): Issue #
- Clean up minor post-extraction source hygiene without changing runtime
behavior.

## 📋 Changes Summary

- Import `KleinanzeigenBot` from its concrete `app` module in the CLI.
- Update stale source comments/docstrings that referenced the previous
package structure.
- No dependencies, configuration changes, CLI output changes,
translation changes, or ad YAML handling 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

* **Chores**
* Refactored internal code organization by adjusting module imports and
updating code documentation references to enhance maintainability and
clarity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-19 08:09:14 +02:00
Jens
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 -->
2026-06-18 21:44:37 +02:00
kleinanzeigen-bot-tu[bot]
d3507c0ff5 chore: Update Python dependencies (#1138)
✔ Update certifi 2026.5.20 -> 2026.6.17 successful
  ✔ Update cyclonedx-python-lib 11.10.0 -> 11.11.0 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-17 22:28:34 +02:00
Jens
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 -->
2026-06-17 22:27:48 +02:00
Jens
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 -->
2026-06-17 14:21:26 +02:00
Jens
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 -->
2026-06-17 13:22:39 +02:00
Jens
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.
2026-06-17 12:21:07 +02:00
Jens
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 -->
2026-06-17 08:13:35 +02:00
Jens
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 -->
2026-06-16 22:06:28 +02:00
Jens
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 -->
2026-06-16 06:57:26 +02:00
Jens
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 -->
2026-06-16 06:16:22 +02:00
Jens
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 -->
2026-06-15 20:51:02 +02:00
Jens
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 -->
2026-06-15 17:34:26 +02:00
Jens
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 -->
2026-06-15 14:10:47 +02:00
Jens
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 -->
2026-06-15 13:34:39 +02:00
Jens
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 -->
2026-06-14 21:42:53 +02:00
kleinanzeigen-bot-tu[bot]
021b887d08 chore: Update Python dependencies (#1124)
✔ Update filelock 3.29.3 -> 3.29.4 successful
  ✔ Update pytest 9.0.3 -> 9.1.0 successful
  ✔ Update pyinstaller 6.20.0 -> 6.21.0 successful
  ✔ Update basedpyright 1.39.7 -> 1.39.8 successful

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-14 14:53:58 +02:00
Jens
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 -->
2026-06-14 11:22:16 +02:00
Jens
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 -->
2026-06-13 23:28:04 +02:00