"""E2E regression: delete a mapping whose SKU+CODMAT contain '/' and '"'. Covers the full real path: render → onclick="...jsAttrEsc(codmat)..." → context menu "Sterge" → deleteMappingConfirm → modal confirm → fetch /api/mappings/delete?sku=&codmat=. Exercises BOTH the slash (routing, query param fix) and the quote (attribute escaping, commit a530ebf) end-to-end. Needs a real Oracle-backed app (seed inserts into ARTICOLE_TERTI with a real CODMAT from NOM_ARTICOLE). When run against the dummy-Oracle subprocess in the plain e2e layer, seeding fails and the test skips — it only asserts in the ./test.sh full (or live :5003) layer. """ import pytest from playwright.sync_api import Page, expect pytestmark = pytest.mark.e2e SLASH_SKU = 'RCR1/4"' def _real_codmat(page: Page, app_url: str): """Fetch a real CODMAT from the Oracle nomenclator, or None if Oracle is down.""" for term in ["01", "PH", "CA"]: resp = page.request.get(f"{app_url}/api/articles/search?q={term}") if resp.ok: results = resp.json().get("results", []) if results: return results[0]["codmat"] return None def test_delete_mapping_with_slash_and_quote(page: Page, app_url: str): codmat = _real_codmat(page, app_url) if not codmat: pytest.skip("Oracle unavailable (no real CODMAT) — slash-delete e2e needs the full layer") # Seed: create the slash+quote mapping. Restore if a prior run soft-deleted it. resp = page.request.post(f"{app_url}/api/mappings", data={"sku": SLASH_SKU, "codmat": codmat, "cantitate_roa": 1}) body = resp.json() if resp.status in (200, 409) else {} if not body.get("success"): if body.get("can_restore"): r = page.request.post( f"{app_url}/api/mappings/restore", params={"sku": SLASH_SKU, "codmat": codmat}) assert r.ok and r.json().get("success"), f"restore seed failed: {r.status} {r.text()}" elif resp.status == 503: pytest.skip("Oracle unavailable (503 on create) — slash-delete e2e needs the full layer") else: pytest.fail(f"could not seed mapping: {resp.status} {resp.text()}") # Collect any 404 on the mappings API during the UI flow. not_found = [] page.on("response", lambda r: not_found.append(r.url) if r.status == 404 and "/api/mappings/" in r.url else None) page.goto(f"{app_url}/mappings") page.wait_for_load_state("networkidle") # Search for the slash+quote SKU. page.fill("#searchInput", SLASH_SKU) page.wait_for_timeout(600) # debounce + reload # The row's context-menu trigger carries the codmat as a data attribute. trigger = page.locator(f'.context-menu-trigger[data-sku="{SLASH_SKU}"]').first expect(trigger).to_be_visible() trigger.click() # Context menu → "Sterge" → confirm modal. page.locator(".context-menu-item", has_text="Sterge").click() confirm = page.locator("#confirmDeleteBtn") expect(confirm).to_be_visible() confirm.click() page.wait_for_timeout(600) # delete fetch + reload # Row is gone and no 404 was hit on the mappings API. expect(page.locator(f'.context-menu-trigger[data-sku="{SLASH_SKU}"]')).to_have_count(0) assert not not_found, f"mappings API returned 404 during delete flow: {not_found}"