"""Cookie-Banner-Tour vor dem Accept-Klick. Öffnet im Banner Settings/Mehr-Optionen, scrollt vertikal, aktiviert jeden Tab, expandet jedes Akkordeon — sodass das Walk-Video den vollen Consent-Katalog (Vendoren, Zwecke, Speicherdauern) zeigt. Wird vom audit_walk_recorder vor `_try_accept_banner` aufgerufen. """ from __future__ import annotations import logging from datetime import datetime, timezone logger = logging.getLogger(__name__) # Tour-Trigger: Öffne im Banner zuerst die Detail-Ansicht / # Einstellungen — DORT findet sich die Vendor-Liste und die Kategorie- # Klappmenüs. _TOUR_OPEN_PHRASES = ( "einstellungen", "individuelle einstellungen", "cookie-einstellungen", "anpassen", "details", "details anzeigen", "mehr optionen", "auswahl anpassen", "verwalten", "konfigurieren", "manage", "customize", "manage cookies", "manage preferences", "settings", "show purposes", "show vendors", "show details", "purposes", "vendors", ) # Banner-Akkordeon/Tab-Pattern: was wir IM Banner aufklappen wollen. _BANNER_EXPAND_SELECTORS = ( "[role=tab]", "[role=tablist] [role=tab]", "[role=tabpanel] button", "[aria-expanded='false']", "button[data-toggle='collapse']", "summary", "details:not([open]) > summary", "[class*=accordion] button", "[class*=expand] button", ".purposes-list button", ".vendor-list button", "[data-testid*='expand']", "[data-testid*='vendor']", "[data-testid*='purpose']", ) def _ts() -> str: return datetime.now(timezone.utc).isoformat() async def tour_cookie_banner(page, max_clicks: int = 35, dwell_ms: int = 700) -> dict: """BEFORE accepting the banner: open Settings, scroll through it, expand every category / vendor / accordion / tab inside. Strategie: 1. Suche Buttons mit Tour-Phrases ("Einstellungen", "Vendor" etc.) — klicken erste Detail-Ansicht. 2. Scrolle Banner vertikal (sodass mehr Inhalte rendern). 3. Aktiviere jedes [role=tab] nacheinander. 4. Expand jedes [aria-expanded=false] / details / summary im Banner-Container. Alles im Video aufgezeichnet — Reviewer sieht den vollen Vendor- und Zweck-Katalog. Best-Effort; jeder Klick mit try/except. """ started = _ts() clicks = 0 notes: list[str] = [] # Phase 1: open "Settings" / "Mehr Optionen" settings_opened = False for phrase in _TOUR_OPEN_PHRASES: try: btn = page.get_by_role("button", name=phrase, exact=False).first if await btn.count() > 0: await btn.scroll_into_view_if_needed(timeout=1500) await btn.click(timeout=2000) await page.wait_for_timeout(1500) notes.append(f"opened: {phrase}") settings_opened = True clicks += 1 break except Exception: continue if not settings_opened: try: link = page.get_by_role("link", name=phrase, exact=False).first if await link.count() > 0: await link.scroll_into_view_if_needed(timeout=1500) await link.click(timeout=2000) await page.wait_for_timeout(1500) notes.append(f"opened (link): {phrase}") settings_opened = True clicks += 1 break except Exception: continue # Phase 2: scroll the banner / dialog to expose more content try: await page.mouse.wheel(0, 600) await page.wait_for_timeout(500) await page.mouse.wheel(0, 600) await page.wait_for_timeout(500) except Exception: pass # Phase 3: activate every [role=tab] in any role=tablist try: tabs = await page.query_selector_all("[role=tab]") for tab in tabs: if clicks >= max_clicks: break try: await tab.scroll_into_view_if_needed(timeout=1000) await tab.click(timeout=1200) await page.wait_for_timeout(dwell_ms) clicks += 1 except Exception: continue if tabs: notes.append(f"tabs activated: {len(tabs)}") except Exception: pass # Phase 4: expand every aria-expanded=false / details / summary for sel in _BANNER_EXPAND_SELECTORS: if clicks >= max_clicks: break try: els = await page.query_selector_all(sel) except Exception: continue for el in els: if clicks >= max_clicks: break try: await el.scroll_into_view_if_needed(timeout=800) await el.click(timeout=1000) await page.wait_for_timeout(dwell_ms) clicks += 1 except Exception: continue return { "timestamp": started, "action": "tour_cookie_banner", "clicks": clicks, "settings_opened": settings_opened, "notes": notes, }