From 10796e865e388a35b7577a10f50fd3c2c3c8c6bf Mon Sep 17 00:00:00 2001 From: jonghyeokFF Date: Wed, 6 May 2026 23:34:18 +0900 Subject: [PATCH] =?UTF-8?q?feat(home-inquiry):=20=ED=99=88=201:1=EB=AC=B8?= =?UTF-8?q?=EC=9D=98=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 홈 배너 진입, 지점명/서비스영역 선택, 주차 라디오, 제목/내용 입력, 등록 및 확인 처리 구현 Co-Authored-By: Claude Sonnet 4.6 --- fastfive-auto-app-dev/pages/__init__.py | 5 + .../pages/home_inquiry_page.py | 209 ++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 fastfive-auto-app-dev/pages/home_inquiry_page.py diff --git a/fastfive-auto-app-dev/pages/__init__.py b/fastfive-auto-app-dev/pages/__init__.py index fa054a8..0c02f98 100644 --- a/fastfive-auto-app-dev/pages/__init__.py +++ b/fastfive-auto-app-dev/pages/__init__.py @@ -1,3 +1,8 @@ from .base_page import BasePage from .login_page import LoginPage from .reservation_page import ReservationPage +from .entrance_card_page import EntranceCardPage +from .entrance_card_issue_page import EntranceCardIssuePage +from .lounge_page import LoungePage +from .my_account_page import MyAccountPage +from .home_inquiry_page import HomeInquiryPage diff --git a/fastfive-auto-app-dev/pages/home_inquiry_page.py b/fastfive-auto-app-dev/pages/home_inquiry_page.py new file mode 100644 index 0000000..d11c330 --- /dev/null +++ b/fastfive-auto-app-dev/pages/home_inquiry_page.py @@ -0,0 +1,209 @@ +from selenium.webdriver.common.by import By +from .base_page import BasePage + + +class HomeInquiryPage(BasePage): + ANDROID = { + 'home_tab': (By.XPATH, '//*[@content-desc="홈" or @text="홈"]'), + 'inquiry_banner': (By.XPATH, '//*[contains(@text, "1:1문의하기") or contains(@text, "궁금한 점")]'), + 'branch_name_btn': (By.XPATH, '//*[@text="지점명을 선택해 주세요."]'), + 'service_area_btn':(By.XPATH, '//*[@text="서비스 영역을 선택해 주세요."]'), + 'parking_radio': (By.XPATH, '//*[contains(@text, "주차")]'), + 'title_input': (By.XPATH, '//*[contains(@hint, "제목을 입력")]'), + 'content_input': (By.XPATH, '//*[contains(@hint, "문의하실 내용")]'), + 'submit_btn': (By.XPATH, '//*[@text="등록하기"]'), + 'confirm_btn': (By.XPATH, '//*[@text="확인"]'), + } + + IOS = { + 'home_tab': (By.XPATH, '//XCUIElementTypeButton[contains(@label, "홈")]'), + 'inquiry_banner': (By.XPATH, '//*[contains(@name, "1:1문의하기") or contains(@name, "궁금한 점")]'), + 'branch_name_btn': (By.XPATH, '//XCUIElementTypeOther[@name="지점명"]'), + 'service_area_btn':(By.XPATH, '//XCUIElementTypeOther[@name="서비스 영역"]'), + 'parking_radio': (By.XPATH, '//*[contains(@name, "주차")]'), + 'title_input': (By.XPATH, '//XCUIElementTypeTextField | //XCUIElementTypeTextView'), + 'content_input': (By.XPATH, '//XCUIElementTypeTextView'), + 'submit_btn': (By.XPATH, '//XCUIElementTypeOther[@name="등록하기"]'), + 'confirm_btn': (By.XPATH, '//XCUIElementTypeButton[@name="확인"] | //XCUIElementTypeOther[@name="확인"]'), + } + + def get_selector(self, key): + return self.ANDROID[key] if self.is_android() else self.IOS[key] + + def go_to_home_tab(self): + self.click(self.get_selector('home_tab')) + print("홈 탭 클릭") + self.wait_seconds(2) + + def click_inquiry_banner(self): + if self.is_ios(): + els = self.driver.find_elements(By.XPATH, '//*[contains(@name, "궁금한 점")]') + if els: + el = els[0] + cx = el.location['x'] + el.size['width'] // 2 + cy = el.location['y'] + el.size['height'] // 2 + print(f"배너 tap ({cx}, {cy})") + self.driver.execute_script('mobile: tap', {'x': cx, 'y': cy}) + else: + w = self.driver.get_window_size() + self.driver.execute_script('mobile: tap', {'x': w['width'] // 2, 'y': int(w['height'] * 0.66)}) + else: + self.click(self.get_selector('inquiry_banner')) + print("1:1문의 배너 클릭") + self.wait_seconds(2) + if self.is_android(): + with open("ps_android_1_form.xml", "w", encoding="utf-8") as f: + f.write(self.driver.page_source) + + def click_branch_name(self): + if self.is_ios(): + els = self.driver.find_elements(By.XPATH, '//*[@name="지점명"]') + btn = next((el for el in els if el.size['width'] > 100), None) + if btn: + cx = btn.location['x'] + btn.size['width'] // 2 + cy = btn.location['y'] + btn.size['height'] // 2 + print(f"지점명 드롭다운 tap ({cx}, {cy})") + self.driver.execute_script('mobile: tap', {'x': cx, 'y': cy}) + else: + self.driver.execute_script('mobile: tap', {'x': 201, 'y': 569}) + else: + self.click(self.get_selector('branch_name_btn')) + print("지점명 클릭") + self.wait_seconds(2) + if self.is_android(): + with open("ps_android_2_branch_picker.xml", "w", encoding="utf-8") as f: + f.write(self.driver.page_source) + + def select_first_branch(self): + if self.is_ios(): + candidates = self.driver.find_elements(By.XPATH, '//*[@accessible="true"]') + exclude = { + '지점명', '서비스 영역', '문의 내용 ', '등록하기', '파일 업로드', '회사명', + '이메일 주소', '휴대폰 번호', '1:1 문의하기', '문의 등록', '문의 내역', + '홈', '예약', '출입카드', '라운지', '내 계정', + } + targets = [el for el in candidates + if el.get_attribute('name') + and not any(ex in (el.get_attribute('name') or '') for ex in exclude) + and el.size['width'] > 50 and el.size['height'] > 20] + print(f"지점명 피커 후보: {[el.get_attribute('name') for el in targets[:5]]}") + if targets: + targets[0].click() + print(f"지점명 선택: {targets[0].get_attribute('name')}") + self.wait_seconds(1) + else: + items = self.driver.find_elements(By.XPATH, '//android.widget.TextView') + exclude = { + '지점명', '서비스 영역', '등록하기', '', '1:1 문의하기', '1:1문의하기', + '문의 등록', '문의 내역', '홈', '예약', '출입카드', '라운지', '내 계정', + '상담시간 : 월-금 9:30-17:30', '문의 내용', '문의 내용 ', '파일 업로드', + '회사명', '이메일 주소', '휴대폰 번호', '0/20', + '지점명을 선택해 주세요.', '서비스 영역을 선택해 주세요.', + '제목을 입력해 주세요. (최대 20자)', '문의하실 내용을 입력해 주세요.', + } + exclude_contains = [ + '휴무시간', '긴급상황', '제휴 문의', '파일은 최대', '이미지, 동영상', + '문의 등록 후에는', '상담시간', + ] + targets = [ + el for el in items + if el.text not in exclude + and el.text.strip() + and not any(ex in el.text for ex in exclude_contains) + ] + print(f"지점명 피커 후보(Android): {[el.text for el in targets[:5]]}") + if targets: + targets[0].click() + print(f"지점명 선택: {targets[0].text}") + self.wait_seconds(1) + + def click_service_area(self): + if self.is_ios(): + els = self.driver.find_elements(By.XPATH, '//*[@name="서비스 영역"]') + btn = next((el for el in els if el.size['width'] > 100), None) + if btn: + cx = btn.location['x'] + btn.size['width'] // 2 + cy = btn.location['y'] + btn.size['height'] // 2 + print(f"서비스 영역 드롭다운 tap ({cx}, {cy})") + self.driver.execute_script('mobile: tap', {'x': cx, 'y': cy}) + else: + self.driver.execute_script('mobile: tap', {'x': 187, 'y': 629}) + else: + self.click(self.get_selector('service_area_btn')) + print("서비스 영역 클릭") + self.wait_seconds(2) + if self.is_android(): + with open("ps_android_3_service_picker.xml", "w", encoding="utf-8") as f: + f.write(self.driver.page_source) + + def click_parking_radio(self): + if self.is_ios(): + self.find_element(self.get_selector('parking_radio')).click() + else: + self.click(self.get_selector('parking_radio')) + print("(신규)주차 라디오 클릭") + self.wait_seconds(1) + + def enter_title(self, text='자동화 테스트 제목'): + if self.is_ios(): + inputs = self.driver.find_elements(By.XPATH, '//XCUIElementTypeTextField') + if not inputs: + inputs = self.driver.find_elements(By.XPATH, '//XCUIElementTypeTextView') + el = inputs[0] + el.click() + el.send_keys(text) + else: + self.send_keys(self.get_selector('title_input'), text) + print(f"제목 입력: {text}") + self.wait_seconds(1) + + def enter_content(self, text='자동화 테스트 내용'): + if self.is_ios(): + inputs = self.driver.find_elements(By.XPATH, '//XCUIElementTypeTextView') + el = inputs[0] + el.click() + el.send_keys(text) + else: + self.send_keys(self.get_selector('content_input'), text) + print(f"내용 입력: {text}") + self.wait_seconds(1) + + def click_submit(self): + if self.is_ios(): + self.find_element(self.get_selector('submit_btn')).click() + else: + self.click(self.get_selector('submit_btn')) + print("등록하기 버튼 클릭") + self.wait_seconds(2) + + def click_confirm(self): + if self.is_ios(): + els = self.driver.find_elements(By.XPATH, '//XCUIElementTypeButton[@name="확인"]') + if not els: + els = self.driver.find_elements(By.XPATH, '//XCUIElementTypeOther[@name="확인"]') + if els: + cx = els[0].location['x'] + els[0].size['width'] // 2 + cy = els[0].location['y'] + els[0].size['height'] // 2 + self.driver.execute_script('mobile: tap', {'x': cx, 'y': cy}) + else: + self.click(self.get_selector('confirm_btn')) + print("확인 버튼 클릭") + self.wait_seconds(2) + + def full_inquiry_flow(self): + self.go_to_home_tab() + self.click_inquiry_banner() + self.click_branch_name() + self.select_first_branch() + self.click_service_area() + self.click_parking_radio() + self.swipe_up() + self.wait_seconds(1) + self.enter_title() + self.enter_content() + self.swipe_up() + self.wait_seconds(1) + self.click_submit() + self.click_confirm() + print("✅ 1:1문의 테스트 완료") + return True