def base_component_list_element(self, ele_class: T) -> List['T']: ''' Because lazy initial finding we need to call __get__ method for finding element :param ele_class: :return: List of WebElement base on element class ''' base_element = ElementFactory.get_list_element( ele_class, self.base_component_locator[0], self.base_component_locator[1]) base_element() return base_element
class ConversationPage(BasePage): path_page = "/admin/conversations" list_conversations_container = ElementFactory.get_list_element( by=By.XPATH, locator="//*[contains(@class, 'list') and contains(@class,'conversations')]//*[contains(@class,'item')]", element_cls=BaseElement ) def __init__(self): self.__loader_component = LoaderComponent() def open_conversation_page(self): self.open_page(path=self.path_page) def get_last_message(self, conversation_index=0): self.__loader_component.wait_for_component_invisible() txt_last_message = self.list_conversations_container[conversation_index].find_element( by=By.TAG_NAME, value="abbr", ) return txt_last_message.text
class SectionWithGroupFAQ(BaseComponent): def __init__(self, section_name): BaseComponent.__init__( self, By.XPATH, f"//div[contains(@class,'knowledge-styles') and contains(@class,'accordion')" f" and .//*[normalize-space(text())='{section_name}']]") base_locator = "//div[contains(@class,'knowledge-styles') and contains(@class,'accordion')]" lbl_knowledge_title = ".//div[contains(@class,'title')]" # /div btn_add_pair = ".//*[contains(@id,'add-pair-button')]" input_question_of_new_group = ".//div[@placeholder='Type a question']" input_answer_of_new_group = "//div[@placeholder='Type a question']/" \ "ancestor::tr//div[@placeholder='Type an answer']" tfoot_tag_name = "tfoot" div_header = ".//div[contains(@class,'title')]" row_contains_answer = ".//tr//div[@placeholder='Type an answer' and .='{0}']/ancestor::tr" row_contains_question = ".//tr/td[1]//span[.='{0}']/ancestor::tr" base_elements = ElementFactory.get_list_element(by=By.XPATH, locator=base_locator, element_cls=BaseElement) def get_section_info(self): lbl_title = self.get_list_child_element(BaseElement, By.XPATH, self.lbl_knowledge_title) result = {"title": lbl_title[0].text, "length": lbl_title[1].text} return result def is_active_section(self): title = self.get_child_element(BaseElement, By.XPATH, ".//div[contains(@class,'title')]") class_name = title.get_attribute('class') return ' active ' in f" {class_name} " def open_section(self): self.base_component_element(BaseElement).click() def collapse_section(self): self.get_child_element(BaseElement, By.XPATH, self.div_header).click() def add_new_group(self, first_question, answer): try: self.get_child_element(BaseElement, By.XPATH, self.btn_add_pair).click() except NoSuchElementException: pass except ElementNotVisibleException: pass self.get_child_element( BaseElement, By.XPATH, self.input_question_of_new_group).send_keys(first_question) self.get_child_element( BaseElement, By.XPATH, self.input_answer_of_new_group).send_keys(answer) self.get_child_element(BaseElement, By.TAG_NAME, self.tfoot_tag_name).click() # time.sleep(1) def add_similar_question(self, answer, similar_question): row = self.find_row_element(answer=answer) btn_locator = ".//*[contains(@id,'add-similar-question')]/preceding-sibling::span" input_locator = ".//div[@placeholder='Type similar question']" # Find button add similar question btn_add_similar_question = self.get_child_element_if_exist( locator_type=By.XPATH, locator_value=btn_locator, parent_element=row) # Click on button add similar question if it exist (btn_add_similar_question is not None) and btn_add_similar_question.click() # Enter similar question row.find_element_by_xpath(input_locator).send_keys(similar_question) row.find_element_by_xpath(input_locator).send_keys(Keys.ENTER) def get_all_manual_faq(self) -> [dict]: result = [] for row in self.find_all_row_element(): questions = self.__get_all_similar_question_in_row(row) if len(questions) > 0: answer = row.find_element_by_xpath( ".//div[@placeholder='Type an answer']").text result.append({"questions": questions, "answer": answer}) return result def get_list_similar_question(self, answer): row = self.find_row_element(answer=answer) return self.__get_all_similar_question_in_row(row) def __get_all_similar_question_in_row(self, row: WebElement): result = [] locator = "./td[1]/div[1]//span[@draggable]" similar_questions = row.find_elements_by_xpath(locator) for question in similar_questions: result.append(question.text) return result def find_row_element(self, answer=None, question=None) -> WebElement: if answer is not None: return self.get_child_element( BaseElement, By.XPATH, self.row_contains_answer.format(answer)) elif question is not None: return self.get_child_element( BaseElement, By.XPATH, self.row_contains_question.format(question)) else: raise Exception("Missing both answer and question") def find_all_row_element(self) -> List[WebElement]: return self.get_list_child_element(BaseElement, By.XPATH, ".//tbody//tr")
class FaqKnowledgeDataTableComponent(BaseComponent): def __init__(self): BaseComponent.__init__( self, By.XPATH, f"//div[contains(@class,'knowledge-styles') and contains(@class,'accordion')]" ) self.__manual_faq_section = SectionWithGroupFAQ('Manual Q&A') base_locator = "//div[contains(@class,'knowledge-styles') and contains(@class,'accordion')" lbl_knowledge_title = ".//div[contains(@class,'title')]" btn_add_pair = ".//*[@id='add-pair-button-1']" base_elements = ElementFactory.get_list_element(by=By.XPATH, locator=base_locator, element_cls=BaseElement) @staticmethod def get_table_component_with_group_faqs(section_name): return SectionWithGroupFAQ(section_name) @staticmethod def get_table_component_with_pair_faqs(section_name): return SectionWithPairFAQ(section_name) @staticmethod def get_number_of_section(): return len(FaqKnowledgeDataTableComponent.base_elements) def get_list_knowledge_title(self) -> List['str']: list_knowledge_title = [] for element in self.get_list_child_element_in_list_component( BaseElement, By.XPATH, self.lbl_knowledge_title): list_knowledge_title.append(element.get_attribute("innerText")) return list_knowledge_title def input_new_manual_question_pair(self, question: str, answer: str): self.__manual_faq_section.add_new_group(question, answer) def modify_question_pair_at_index(self, index, question, answer): # TODO(namndoan): Update this when question field become editable pass def click_on_data_table_with_faq_url(self, faq_url): section_for_faq_url = SectionWithGroupFAQ(faq_url) section_for_faq_url.open_section() def get_all_question_pair_data_in_table(self): self.__manual_faq_section.wait_for_component_visible() return self.__manual_faq_section.get_all_manual_faq() def get_all_faq_data_in_table_with_faq_url(self, faq_url): section_for_faq_url = SectionWithGroupFAQ(faq_url) section_for_faq_url.wait_for_component_visible() # Open the section if it is not active if not section_for_faq_url.is_active_section(): section_for_faq_url.open_section() return section_for_faq_url.get_all_manual_faq() def wait_for_manual_faq_section_visible(self): return self.__manual_faq_section.wait_for_component_visible() def get_number_of_manual_pair(self) -> str: return self.__manual_faq_section.get_section_info()["length"]
class ClientSimulator(BaseComponent): txt_feedback_title_locator = "//*[contains(@class, 'feedbackContainer')]/div[contains(@class, 'title')]" ico_open_bot = ElementFactory.get_element_until( by=By.XPATH, locator="//div[contains(@class, 'minimizedBoxContainer')]", wait_type=EC.visibility_of_element_located, element_cls=BaseElement ) chat_box = ElementFactory.get_element_until( by=By.XPATH, locator="//div[contains(@class, 'chatboxContainer')]", wait_type=EC.visibility_of_element_located, element_cls=BaseElement ) ico_close_bot = ElementFactory.get_element_until( by=By.XPATH, locator="//div[contains(@class, 'chatboxContainer')]//div[contains(@class, 'minimizeIcon')]", wait_type=EC.visibility_of_element_located, element_cls=BaseElement ) txb_message = ElementFactory.get_element_until( by=By.XPATH, locator="//div[contains(@class, 'chatboxContainer')]//textarea", wait_type=EC.visibility_of_element_located, element_cls=BaseElement ) list_txt_response = ElementFactory.get_list_element( by=By.XPATH, locator="//div[contains(@class,'messageContainerLeft')]//div[contains(@class,'messageTextLeft')]", element_cls=BaseElement ) ifr_sample_app_chatbox = ElementFactory.get_element_until( by=By.CSS_SELECTOR, locator="#sample_app-minimized-box-frame", element_cls=BaseElement, wait_type=EC.visibility_of_element_located ) ifr_chat_box = ElementFactory.get_element_until( by=By.ID, locator="sample_app-chatbox-frame", wait_type=EC.visibility_of_element_located, element_cls=BaseElement ) txt_feedback_title = ElementFactory.get_element_until( by=By.XPATH, locator=txt_feedback_title_locator, wait_type=EC.visibility_of_element_located, element_cls=BaseElement ) list_feedback_icon = ElementFactory.get_list_element( by=By.XPATH, locator="//*[contains(@class, 'feedbackContainer')]/div[contains(@class, 'ratingIconsContainer')]/*", element_cls=BaseElement ) txt_latest_response = ElementFactory.get_element_until( by=By.XPATH, locator="(//*[contains(@class, 'styles__messageTextRight')])[last()]" "/../../../following-sibling::div[1]//*[contains(@class, 'styles__messageTextLeft')]", wait_type=EC.visibility_of_element_located, element_cls=BaseElement ) btn_send_msg = ElementFactory.get_element_until( by=By.XPATH, locator="//div[contains(@class, 'chatboxContainer')]//textarea/../../following-sibling::button", wait_type=EC.visibility_of_element_located, element_cls=BaseElement ) def __init__(self): BaseComponent.__init__(self, By.ID, "sample_app-chatbox") def open_chat_box(self): self.click_on_chatbot_icon() def close_chat_box(self): self.click_on_chatbot_icon() def click_on_chatbot_icon(self): self.driver.core_driver.switch_to.frame(self.ifr_sample_app_chatbox.get_wrapper_element) self.ico_open_bot.click() self.driver.core_driver.switch_to.parent_frame() def is_open(self): try: is_open = self.ifr_chat_box.is_displayed() except TimeoutException: is_open = False return is_open def send_message(self, message): if not self.is_open(): self.open_chat_box() self._switch_to_chat_frame() self.txb_message.send_keys(message) self.btn_send_msg.click() self._switch_to_parent_frame() def get_responses(self): self._switch_to_chat_frame() responses = list(map(lambda elem: elem.get_element_attribute("innerText"), self.list_txt_response)) self._switch_to_parent_frame() return responses def create_stub_messages(self, message, quantity=1): self.open_chat_box() self._switch_to_chat_frame() for i in range(quantity): self.txb_message.send_keys(message + " " + str(i)) self.btn_send_msg.click() self.wait_for_response() self._switch_to_parent_frame() def wait_for_feedback_returned(self): self._switch_to_chat_frame() try: is_visible = self.txt_feedback_title.is_displayed() except TimeoutException: is_visible = False self._switch_to_parent_frame() return is_visible def wait_for_response(self): # Trigger wait method after each message is sent by calling the element return self.txt_latest_response def get_feedback_title(self): self._switch_to_chat_frame() text = self.txt_feedback_title.get_element_text() self._switch_to_parent_frame() return text def get_list_feedback_text(self): feedback_text = [] self._switch_to_chat_frame() for element in self.list_feedback_icon: action = ActionChains(self.driver.core_driver).move_to_element(element.get_wrapper_element) action.perform() # Relocated element due to change in DOM ele = self.driver.core_driver.find_element_by_xpath(self.txt_feedback_title_locator) feedback_text.append(ele.text) self._switch_to_parent_frame() return feedback_text def _switch_to_chat_frame(self): self.driver.core_driver.switch_to.frame(self.ifr_chat_box.get_wrapper_element) def _switch_to_parent_frame(self): self.driver.core_driver.switch_to.parent_frame() def open_wait_close(self): pass def get_bot_avatar_source(self): self._switch_to_chat_frame() result = self.driver.core_driver.find_element_by_xpath("//img").get_attribute("src") self._switch_to_parent_frame() return result