From 08050f2af0c59a5180a0be4f8a0f593f39adfac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 2 Feb 2026 09:10:38 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20GPS=20=EA=B8=B0=EB=B0=98=20=EC=8B=9C?= =?UTF-8?q?=EB=82=98=EB=A6=AC=EC=98=A4=EC=97=90=20=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=ED=8C=9D=EC=97=85=20=ED=81=B4=EB=A6=AD=20?= =?UTF-8?q?=EB=8B=A8=EA=B3=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Geolocation API 모킹을 첫 번째 단계로 이동 (팝업 방지) - '사이트에 있는 동안 허용' 버튼 클릭 로직 추가 - 좌측 상단 권한 팝업 처리 스크립트 개선 Co-Authored-By: Claude Opus 4.5 --- attendance-checkin.json | 81 ++++++++++++++++---------------------- attendance-management.json | 37 ++++++++++++++++- 2 files changed, 70 insertions(+), 48 deletions(-) diff --git a/attendance-checkin.json b/attendance-checkin.json index 1169fa2..1aaf604 100644 --- a/attendance-checkin.json +++ b/attendance-checkin.json @@ -92,53 +92,46 @@ "steps": [ { "id": "step-0", - "name": "🔐 위치 권한 사전 설정 (Playwright)", - "description": "Playwright 컨텍스트 레벨에서 위치 권한을 미리 허용하여 브라우저 팝업 방지", + "name": "🔐 Geolocation API 모킹 (권한 팝업 방지)", + "description": "페이지 로드 직후 Geolocation API를 모킹하여 브라우저 권한 팝업이 나타나지 않도록 함", "critical": true, - "executeBeforeNavigation": true, - "playwright": { - "commands": [ - { - "tool": "mcp__playwright__playwright_evaluate", - "script": "// 브라우저 시작 시 위치 권한 자동 허용 (Playwright 컨텍스트 레벨)" - } - ], - "contextSetup": { - "permissions": ["geolocation"], - "geolocation": { - "latitude": 37.557358, - "longitude": 126.864414 - } - } - }, - "note": "이 단계는 브라우저 컨텍스트 생성 시 자동으로 처리됨" + "executeBeforeNavigation": false, + "executeImmediately": true, + "actions": [ + { + "type": "evaluate", + "script": "(() => { const mockPosition = { coords: { latitude: 37.557358, longitude: 126.864414, accuracy: 100, altitude: null, altitudeAccuracy: null, heading: null, speed: null }, timestamp: Date.now() }; const mockGeolocation = { getCurrentPosition: (success, error, options) => { console.log('[E2E] Geolocation.getCurrentPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); }, watchPosition: (success, error, options) => { console.log('[E2E] Geolocation.watchPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); return 1; }, clearWatch: (id) => {} }; Object.defineProperty(navigator, 'geolocation', { value: mockGeolocation, writable: false, configurable: true }); console.log('[E2E] Geolocation API 모킹 완료 - 서울 영등포구 좌표'); return { success: true, coords: mockPosition.coords }; })()", + "description": "Geolocation API 모킹 (서울 영등포구 좌표: 37.557358, 126.864414)" + }, + { "type": "wait", "duration": 300, "description": "모킹 적용 대기" } + ], + "note": "Geolocation API를 모킹하면 브라우저가 위치 권한을 요청하지 않음" }, { "id": "step-0-1", - "name": "🗺️ 위치 권한 팝업 처리 및 사이드바 메뉴 전체 펼치기", - "description": "위치 권한 요청 팝업 처리 후 모두 펼치기 버튼을 클릭하여 전체 메뉴 펼침", + "name": "🗺️ 브라우저 위치 권한 팝업 클릭 (좌측 상단)", + "description": "Chrome 브라우저 좌측 상단에 나타나는 '사이트에 있는 동안 허용' 팝업 클릭", "critical": true, "actions": [ - { "type": "wait", "duration": 1000, "description": "페이지 로드 및 권한 팝업 표시 대기" }, + { "type": "wait", "duration": 1500, "description": "위치 권한 팝업 표시 대기" }, { - "type": "conditionalClick", - "description": "위치 권한 팝업이 나타나면 '허용' 또는 '항상 허용' 버튼 클릭", - "selectors": [ - "button:has-text('항상 허용')", - "button:has-text('허용')", - "button:has-text('Allow')", - "button:has-text('Always Allow')", - "[data-testid='allow-location']", - ".permission-allow-button", - "button[aria-label*='허용']", - "button[aria-label*='위치']" - ], - "fallback": { - "action": "skip", - "reason": "권한 팝업이 없으면 이미 허용된 상태" - } + "type": "evaluate", + "script": "(async function() { const permissionSelectors = [ '[class*=\"permission\"][class*=\"allow\"]', '[class*=\"infobar\"] button', '[aria-label*=\"허용\"]', '[aria-label*=\"Allow\"]', 'button:has-text(\"사이트에 있는 동안 허용\")', 'button:has-text(\"허용\")', 'button:has-text(\"Allow\")', '[data-testid*=\"permission\"]', '.permission-prompt button', '[class*=\"PermissionPrompt\"] button' ]; for (const sel of permissionSelectors) { try { const btn = document.querySelector(sel); if (btn && btn.offsetParent !== null) { btn.click(); console.log('[E2E] 위치 권한 팝업 클릭 성공:', sel); await new Promise(r => setTimeout(r, 500)); return { clicked: true, selector: sel }; } } catch(e) {} } const allButtons = Array.from(document.querySelectorAll('button, [role=\"button\"]')); const allowBtn = allButtons.find(b => { const text = b.innerText || b.textContent || ''; return text.includes('사이트에 있는 동안 허용') || text.includes('허용') || text.includes('Allow'); }); if (allowBtn && allowBtn.offsetParent !== null) { allowBtn.click(); console.log('[E2E] 위치 권한 팝업 텍스트 검색으로 클릭'); return { clicked: true, method: 'textSearch' }; } console.log('[E2E] 위치 권한 팝업 없음 (이미 허용되었거나 모킹으로 우회됨)'); return { clicked: false, reason: 'no_popup_found' }; })()", + "description": "좌측 상단 권한 팝업 찾아서 클릭" }, - { "type": "wait", "duration": 500, "description": "권한 설정 적용 대기" }, + { "type": "wait", "duration": 500, "description": "권한 설정 적용 대기" } + ], + "errorHandling": { + "onTimeout": "continue", + "onNotFound": "continue", + "reason": "팝업이 없으면 이미 허용된 상태로 간주" + } + }, + { + "id": "step-0-2", + "name": "📂 사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴 펼침", + "actions": [ { "type": "evaluate", "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" @@ -152,14 +145,8 @@ { "type": "screenshot", "name": "after_permission_grant_and_menu_expanded" } ], "verification": [ - "위치 권한 팝업이 닫혔는지 확인", - "또는 팝업이 처음부터 없었는지 확인 (이미 허용된 경우)" - ], - "errorHandling": { - "onTimeout": "continue", - "onNotFound": "continue", - "reason": "팝업이 없으면 권한이 이미 허용된 상태로 간주" - } + "사이드바 메뉴가 펼쳐졌는지 확인" + ] }, { "id": 2, diff --git a/attendance-management.json b/attendance-management.json index 78a9b39..7262b1e 100644 --- a/attendance-management.json +++ b/attendance-management.json @@ -81,7 +81,42 @@ "steps": [ { "id": "step-0", - "name": "사이드바 메뉴 전체 펼치기", + "name": "🔐 Geolocation API 모킹 (권한 팝업 방지)", + "description": "페이지 로드 직후 Geolocation API를 모킹하여 브라우저 권한 팝업이 나타나지 않도록 함", + "critical": true, + "actions": [ + { + "type": "evaluate", + "script": "(() => { const mockPosition = { coords: { latitude: 37.5665, longitude: 126.9780, accuracy: 100, altitude: null, altitudeAccuracy: null, heading: null, speed: null }, timestamp: Date.now() }; const mockGeolocation = { getCurrentPosition: (success, error, options) => { console.log('[E2E] Geolocation.getCurrentPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); }, watchPosition: (success, error, options) => { console.log('[E2E] Geolocation.watchPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); return 1; }, clearWatch: (id) => {} }; Object.defineProperty(navigator, 'geolocation', { value: mockGeolocation, writable: false, configurable: true }); console.log('[E2E] Geolocation API 모킹 완료 - 서울시청 좌표'); return { success: true, coords: mockPosition.coords }; })()", + "description": "Geolocation API 모킹 (서울시청 좌표: 37.5665, 126.9780)" + }, + { "type": "wait", "duration": 300, "description": "모킹 적용 대기" } + ], + "note": "Geolocation API를 모킹하면 브라우저가 위치 권한을 요청하지 않음" + }, + { + "id": "step-0-1", + "name": "🗺️ 브라우저 위치 권한 팝업 클릭 (좌측 상단)", + "description": "Chrome 브라우저 좌측 상단에 나타나는 '사이트에 있는 동안 허용' 팝업 클릭", + "critical": true, + "actions": [ + { "type": "wait", "duration": 1500, "description": "위치 권한 팝업 표시 대기" }, + { + "type": "evaluate", + "script": "(async function() { const permissionSelectors = [ '[class*=\"permission\"][class*=\"allow\"]', '[class*=\"infobar\"] button', '[aria-label*=\"허용\"]', '[aria-label*=\"Allow\"]', 'button:has-text(\"사이트에 있는 동안 허용\")', 'button:has-text(\"허용\")', 'button:has-text(\"Allow\")', '[data-testid*=\"permission\"]', '.permission-prompt button', '[class*=\"PermissionPrompt\"] button' ]; for (const sel of permissionSelectors) { try { const btn = document.querySelector(sel); if (btn && btn.offsetParent !== null) { btn.click(); console.log('[E2E] 위치 권한 팝업 클릭 성공:', sel); await new Promise(r => setTimeout(r, 500)); return { clicked: true, selector: sel }; } } catch(e) {} } const allButtons = Array.from(document.querySelectorAll('button, [role=\"button\"]')); const allowBtn = allButtons.find(b => { const text = b.innerText || b.textContent || ''; return text.includes('사이트에 있는 동안 허용') || text.includes('허용') || text.includes('Allow'); }); if (allowBtn && allowBtn.offsetParent !== null) { allowBtn.click(); console.log('[E2E] 위치 권한 팝업 텍스트 검색으로 클릭'); return { clicked: true, method: 'textSearch' }; } console.log('[E2E] 위치 권한 팝업 없음 (이미 허용되었거나 모킹으로 우회됨)'); return { clicked: false, reason: 'no_popup_found' }; })()", + "description": "좌측 상단 권한 팝업 찾아서 클릭" + }, + { "type": "wait", "duration": 500, "description": "권한 설정 적용 대기" } + ], + "errorHandling": { + "onTimeout": "continue", + "onNotFound": "continue", + "reason": "팝업이 없으면 이미 허용된 상태로 간주" + } + }, + { + "id": "step-0-2", + "name": "📂 사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ {