cadrumo.adapters.outbound.aeat._representation_gate module

AEAT own-name representation-gate logic, shared across flows.

AEAT’s representation-choice screen – where the authenticated profile must act in its own name rather than as a represented third party – appears on more than one authenticated flow: Cl@ve Móvil login (adapters.outbound.aeat.auth) and the IVA compensation wallet reader (adapters.outbound.aeat.sede) both drive through it. Both sites independently implemented the same logic before this module existed, with no cross-reference between them (round-3 semantic duplicate discovery, this session); this is now the single implementation for all three pieces of that screen:

  • wait_for_own_name_representation_selector() – waiting for AEAT to render the own-name selector, parameterised by the settings-derived selector strings and by the caller’s own error identity, so AeatLoginAssertionError and SedeNavigationError remain distinct domain errors rather than being collapsed into one.

  • continue_button_selectors() / click_first_matching_selector() – the continue-button selector-fallback chain for AEAT’s pre303 alert modal, parameterised by scoped_to_shown so each caller keeps its own selector-matching scope.

  • dismiss_pre303_alert_modal_if_present() – the alert-modal dismissal itself, using the auth caller’s "show"-class predicate as the single canonical check (operator directive: the wallet’s prior raw substring test, with no class-state check at all, was ruled the incorrect half of the pair and deleted outright, not merged as an alternative). See that function’s own docstring for the residual, still-open evidence gap this ruling accepts rather than resolves.

own_name_representation_selectors(*selectors)[source]

Return selectors, order-preserving, blank-and-duplicate-free.

Return type:

tuple[str, ...]

Parameters:

selectors (str)

async wait_for_own_name_representation_selector(page, *, own_name_label_selector, own_name_selector, probe_timeout_ms, raise_configuration_error)[source]

Return the configured own-name selector that AEAT renders first.

Tries own_name_label_selector then own_name_selector (deduped, in that order), waiting up to probe_timeout_ms for each candidate via Playwright’s wait_for_selector. A PlaywrightError raised by the last-tried candidate propagates unchanged – that is a real navigation failure, not a configuration problem. raise_configuration_error is called (and must raise) only for the two configuration-shaped failures: page exposing no wait_for_selector capability, or both selector strings being blank.

Parameters:
  • page (object) – A Playwright Page or a page-like object exposing an async wait_for_selector(selector, *, timeout=...) method.

  • own_name_label_selector (str) – The primary (label) selector to try first.

  • own_name_selector (str) – The fallback (input) selector.

  • probe_timeout_ms (float) – Per-selector wait timeout in milliseconds.

  • raise_configuration_error (Callable[[str], NoReturn]) – Called with a diagnostic message to raise the caller’s own domain error for a configuration-shaped failure.

Return type:

str

Returns:

The selector string that resolved.

continue_button_selectors(modal_selector, button_text, *, scoped_to_shown)[source]

Return continue-button selector variants for an AEAT alert modal.

Generates the title-case and as-given renderings of button_text plus a generic .modal-footer fallback for a button whose own text does not match either variant (the auth caller’s original third selector). When scoped_to_shown is True every selector is qualified with .show, so it matches only while the modal carries that CSS state – the auth caller’s convention, applied there only after it has already gated on the modal’s "show" class itself. scoped_to_shown=False (the wallet caller) omits the qualifier, preserving that caller’s existing unconditional match – this function changes what is TRIED, never what is REQUIRED to be present for a click to succeed.

Parameters:
  • modal_selector (str) – The modal container’s CSS selector (e.g. "#alertsModal").

  • button_text (str) – The configured continue-button text to match.

  • scoped_to_shown (bool) – Whether every generated selector is qualified with .show.

Return type:

tuple[str, ...]

Returns:

A tuple of candidate selectors, in try-order, deduplicated.

class ClickablePage(*args, **kwargs)[source]

Bases: Protocol

The one page capability click_first_matching_selector() requires.

Declared rather than accepting object and reaching through it: the function needs exactly one method, and saying so lets a caller pass any page that provides it while keeping the call site checkable.

async click(selector)[source]
Return type:

None

Parameters:

selector (str)

async click_first_matching_selector(page, selectors)[source]

Click the first selector in selectors that resolves.

Tries each selector via page.click in order, catching PlaywrightError and trying the next; re-raises the LAST error only once every candidate has failed. A single-element selectors tuple reproduces raise-on-first-miss exactly (today’s wallet behaviour); a multi-element tuple adds fallback attempts strictly on top of that – this never removes a selector that used to be tried, only adds more chances to succeed before giving up.

Parameters:
  • page (ClickablePage) – A Playwright Page or a page-like object exposing an async click(selector) method (both current callers’ page types declare this unconditionally, unlike wait_for_selector above).

  • selectors (tuple[str, ...]) – Candidate selectors, in try-order.

Raises:

PlaywrightError – If every selector failed to resolve.

Return type:

None

async dismiss_pre303_alert_modal_if_present(page, *, alert_modal_selector, alert_continue_button_text, on_declined_hidden_modal=None)[source]

Dismiss AEAT’s pre303 alert modal, but only when it genuinely carries "show".

Collapses the two independent implementations this session found (round-3 semantic duplicate discovery): the auth caller checked the modal’s "show" CSS class before acting; the wallet caller did a raw substring test for the modal id anywhere in the page HTML, with no class-state check at all, so it could not distinguish a modal that is genuinely up from one merely present in the DOM. Four-cell characterisation (sede/tests/test_pre303_alert_modal_divergence.py) proved the divergence rather than arguing it: the wallet path clicked in BOTH the shown and the present-but-hidden state, the auth path only in the shown state. Per operator directive, the auth predicate is canonical; the wallet’s substring-only version is deleted, not kept as an alternative.

The residual risk this collapse accepts, not resolves: no recorded AEAT capture of this modal exists in either visibility state, so the class-check predicate is verified against a synthetic fixture built from the two implementations’ own selector-construction code, not against AEAT’s real page. The class-check being the legally/structurally correct question to ask (“is this modal actually up”) is not the same claim as “this is how AEAT’s real markup behaves”. If the wallet’s pre303 modal can render shown WITHOUT the "show" class (a different visibility mechanism on that specific page), this collapse would make the wallet decline to dismiss a modal it must dismiss – trading the demonstrated over-click risk for an undemonstrated hang risk. That is why a decline caused by on_declined_hidden_modal must surface, not pass quietly: the evidence that would close this gap for good is a real capture of the modal in both states, or an observed live run, neither of which exists yet.

Parameters:
  • page (object) – A Playwright Page or a page-like object exposing async content() and click(selector) methods.

  • alert_modal_selector (str) – The modal container’s CSS selector (e.g. "#alertsModal").

  • alert_continue_button_text (str) – The configured continue-button text.

  • on_declined_hidden_modal (Callable[[], None] | None) – Called (never awaited, never passed arguments) when the modal element is present in the DOM but does not carry "show", so this function declines to click and returns without acting. None (the default) declines silently, matching the auth caller’s original behaviour, which this collapse does not change; the wallet caller MUST supply a diagnostic callback, because declining is new behaviour there and must be visible in its output rather than read off a stack trace three layers away.

Return type:

None