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, soAeatLoginAssertionErrorandSedeNavigationErrorremain 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 byscoped_to_shownso 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.
- 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_selectorthenown_name_selector(deduped, in that order), waiting up toprobe_timeout_msfor each candidate via Playwright’swait_for_selector. APlaywrightErrorraised by the last-tried candidate propagates unchanged – that is a real navigation failure, not a configuration problem.raise_configuration_erroris called (and must raise) only for the two configuration-shaped failures:pageexposing nowait_for_selectorcapability, or both selector strings being blank.- Parameters:
page (
object) – A PlaywrightPageor a page-like object exposing an asyncwait_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:
- 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_textplus a generic.modal-footerfallback for a button whose own text does not match either variant (the auth caller’s original third selector). Whenscoped_to_shownis 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:
- Return type:
- Returns:
A tuple of candidate selectors, in try-order, deduplicated.
- class ClickablePage(*args, **kwargs)[source]¶
Bases:
ProtocolThe one page capability
click_first_matching_selector()requires.Declared rather than accepting
objectand 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_first_matching_selector(page, selectors)[source]¶
Click the first selector in
selectorsthat resolves.Tries each selector via
page.clickin order, catchingPlaywrightErrorand trying the next; re-raises the LAST error only once every candidate has failed. A single-elementselectorstuple 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 PlaywrightPageor a page-like object exposing an asyncclick(selector)method (both current callers’ page types declare this unconditionally, unlikewait_for_selectorabove).selectors (
tuple[str,...]) – Candidate selectors, in try-order.
- Raises:
PlaywrightError – If every selector failed to resolve.
- Return type:
- 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 byon_declined_hidden_modalmust 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 PlaywrightPageor a page-like object exposing asynccontent()andclick(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: