Commit Graph

11 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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