diff --git a/A05_EduDetail/A05_EduDetail_Guide_Capture.py b/A05_EduDetail/A05_EduDetail_Guide_Capture.py index 786d10319..1277d7cf3 100644 --- a/A05_EduDetail/A05_EduDetail_Guide_Capture.py +++ b/A05_EduDetail/A05_EduDetail_Guide_Capture.py @@ -49,8 +49,9 @@ frame 은 callouts 항목에도 씀 지킴 - - [저장] · [확정] · [초기화] · [적용] · [삭제] · 메일 보내기 등 위험 단추 누름은 거절 — 선택자 · eval 글(목록 - 읽을 때) · 실제 요소 글자(누르기 직전) 둘 다 검사 · [취소] · [닫기] 는 통과 + - [저장] · [확정] · [초기화] · [적용] · [삭제] · 메일 보내기 등 위험 단추 누름은 거절 — 선택자 속 글자 + (`text=` · `:has-text("…")`만 · class · id 이름은 안 봄) · eval 글 · 실제 요소 글자(누르기 직전) 검사 · + [취소] · [닫기] 는 통과 - 확인창을 찍을 때만 `"confirm": true` 로 그 단추를 누름 — 그 장면은 after 에 누름 단계가 있어야 함([취소]) - 페이지의 /api 쓰기 요청(PUT · PATCH · DELETE · allow_post 밖 POST)은 모두 끊음 · 끊은 요청은 표에 적음 - 브라우저 확인창(window.confirm)은 늘 취소 · 새 컨텍스트라 사용자 브라우저 · 저장소와 안 섞임 @@ -132,6 +133,17 @@ def is_danger(text: str) -> bool: return bool(text) and bool(DANGER_RE.search(text)) and not SAFE_RE.search(text) +SEL_TEXT_RE = re.compile( + r"""text\s*=\s*(?:"[^"]*"|'[^']*'|[^>]+)|:(?:has-)?text(?:-is|-matches)?\(\s*(?:"[^"]*"|'[^']*')""", + re.I, +) + + +def sel_text(sel: str) -> str: + """선택자 속 글자 부분만(`text=` · `:has-text("…")`) — class · id 이름은 뺌.""" + return " ".join(SEL_TEXT_RE.findall(sel or "")) + + def _check_steps(steps: Any, where: str) -> tuple[list[str], bool]: """단계 목록 검사 → (잘못, confirm 단계 있음).""" if steps is None: @@ -166,11 +178,11 @@ def _check_steps(steps: Any, where: str) -> tuple[list[str], bool]: if kind != "click": errs.append(f"{at} confirm 은 click 에만") confirm = True - elif kind in ("click", "eval") and is_danger(val): + elif is_danger(val if kind == "eval" else sel_text(val)) and kind in ("click", "eval"): errs.append( f"{at} 위험 단추 거절({kind}: {val[:40]}) — 확인창만이면 confirm + after [취소]" ) - if kind == "press" and is_danger(st.get("sel", "")): + if kind == "press" and is_danger(sel_text(st.get("sel", ""))): errs.append(f"{at} 위험 요소에서 키 거절") return errs, confirm diff --git a/resources/tester/test_539_guide_capture.py b/resources/tester/test_539_guide_capture.py index d94fee832..3d32f2297 100644 --- a/resources/tester/test_539_guide_capture.py +++ b/resources/tester/test_539_guide_capture.py @@ -80,11 +80,9 @@ def test_bad_fields(bad): [ "button:has-text('저장')", "text=확정", - ".reset-btn", "button:has-text('초기화')", "button:has-text('적용')", "button:has-text('메일 보내기')", - "#save", "button:has-text('삭제')", ], ) @@ -101,7 +99,16 @@ def test_danger_eval_rejected(): @pytest.mark.parametrize( - "sel", ["button:has-text('취소')", "text=닫기", ".cancel", "button:has-text('내보내기')"] + "sel", + [ + "button:has-text('취소')", + "text=닫기", + ".cancel", + "button:has-text('내보내기')", + ".d04-confirm__list", + ".ui-confirm__cancel", + "#save-btn button:has-text('닫기')", + ], ) def test_safe_click_passes(sel): assert G.check_scene(_scene(before=[{"click": sel}]), "B05") == []