def open_item_by_name_in_main_menu(self, name: str) -> None: """ Открыть раздел сайта по имени в главном меню """ assert name in self.main_menu, f"Нет пункта {name} в главном меню" txb_menu_item = BaseElement( self.base_admin_locators['txbMenuItemByName'].format(name)) txb_menu_item.click()
def open_site_by_link_in_header(self) -> None: """ Открытие главной страницы сайта по ссылке из шапки """ tabs_in_browser = self._browser.window_handles txb_site_name_link = BaseElement( self.base_admin_locators['txbSiteNameLink']) txb_site_name_link.click() WebDriverWait(self._browser, 10).until( lambda browser: len(browser.window_handles) > len(tabs_in_browser))
def __init__(self, browser: webdriver): self._browser = browser self.products = [] self.errors = [] div_products = self._browser.find_elements_by_xpath(self.locators['divProducts']) for div_product in div_products: html = etree.parse(StringIO(div_product.get_property('outerHTML')), parser=etree.HTMLParser()) # Название товара name_element = BaseElement(self.locators['txbName'], context=div_product) # Цена за единицу продукта price_per_product = html.xpath(self.locators['txbPricePerProduct'])[0].text # Поле с количеством quantity_element = BaseElement(self.locators['inpQuantity'], context=div_product) # Итогова цена за товар price_amount = convert_price_from_str_to_float(html.xpath(self.locators['txbPriceAmount'])[0].text) try: # Итоговая цена без скидки price_amount_old = convert_price_from_str_to_float( html.xpath(self.locators['txbPriceAmountOld'])[0].text) except IndexError: price_amount_old = None # Элемент удаления товара из корзины delete_element = BaseElement(self.locators['btnDelete'], context=div_product) # Сообщение о количестве товара в магазине try: count_message = html.xpath(self.locators['txbProductCount'])[0].text except IndexError: count_message = None self.products.append({ 'name': name_element.text + self._get_params(html), # название товара 'price_per_product': convert_price_from_str_to_float(price_per_product), # цена за единицу продукта 'quantity': int(quantity_element.ge().get_property('value')), # количество товара 'price_amount': price_amount, # итогова цена за товар 'price_amount_old': price_amount_old, # итоговая цена без скидки 'count_message': count_message, # сообщение о количеством товара в магазине 'elements': { 'name': name_element, # элемент название товара 'quantity': quantity_element, # элемент количество товара 'delete': delete_element # элемент удаления товара из корзины } }) self._error_in_quantity(self.products[-1]['name'], quantity_element, count_message)
def open_item_by_names_in_left_menu(self, menu_item: str, sub_menu_item: str) -> None: """ Открыть раздел сайта по имени раздела и пункта в левом меню """ menu_item_parent_element = BaseElement( self.base_admin_shop_locators['txbMenu'].format(menu_item) + "/..") if 'is-open' not in menu_item_parent_element.ge().get_attribute( 'class'): # Если родительская категория не раскрыта, раскрываю BaseElement(self.base_admin_shop_locators['txbMenu'].format( menu_item)).click() # Открываю подменю BaseElement(self.base_admin_shop_locators['txbSubMenu'].format( sub_menu_item)).click()
def __init__(self, browser: webdriver): self._browser = browser self.deliveries = [] div_deliveries = self._browser.find_elements_by_xpath(self.locators['divDeliveries']) for div_delivery in div_deliveries: html = etree.parse(StringIO(div_delivery.get_property('outerHTML')), parser=etree.HTMLParser()) # Название метода name_element = BaseElement(self.locators['txbName'], context=div_delivery) # Стоимость доставки price = convert_price_from_str_to_float(html.xpath(self.locators['txbPrice'])[0].text) # Стоимость доставки без скидки try: old_price = convert_price_from_str_to_float(html.xpath(self.locators['txbPriceOld'])[0].text) except IndexError: old_price = None self.deliveries.append({ 'name': name_element.text, # название метода 'price': price, # стоимость 'old_price': old_price, # стоимость без скидки 'elements': { 'name': name_element # элемент названия метода } })
def get_applied_name(self) -> str: """ Получить имя примененного промокода """ try: return BaseElement(self.locators['inpField'], 2, True).ge().get_attribute('value') except Exception: return ""
def is_it_empty_shop_cart_page(self) -> bool: """ Проверяем что открыта пустая корзина """ try: BaseElement(self.locators['divEmptyShopCartPage'], 1) return True except AssertionError: return False
def is_it_the_right_page(self) -> bool: """ Определяем что текущая страница правильная, если на ней есть элемент page_locator """ try: BaseElement(self.page_locator, wait_seconds=20) return True except AssertionError: return False
def is_applied(self) -> bool: """ Проверяем, что промокод применен """ try: BaseElement(self.locators['txbMessage'], 1) return True except AssertionError: return False
def is_order_number_exist(self, order_number: str) -> bool: """ Проверка существования заказа с заданным номером """ try: BaseElement(self.locators['txbOrderNumber'].format(value=order_number)) return True except NoSuchElementException: return False
def delete_by_name(self, product_name: str) -> None: """ Удалить указанный товар """ time.sleep(1) self.products[self._get_index_by_name(product_name)]['elements']['delete'].click() time.sleep(2) # Подтверждения удаления товара BaseElement(self.locators['btnConfirmDelete']).click() FrontShopCartPage(is_wait=True)
def submit_form(self) -> bool: """ Оформляем заказ """ FrontShopCartPage(is_wait=True) try: BaseElement(self.locators['btnSubmit'], 2).click() return True except ElementClickInterceptedException: return False
def activate(self, promo_code: str) -> None: """ Активировать промокод """ # Проверяем, что в корзине нет активированного промокода applied_promo_code = self.get_applied_name() assert not applied_promo_code, f"В корзине уже активирован промокод '{applied_promo_code}'" # Если отображается текст, активируем поле ввода промокода try: BaseElement(self.locators['txbLink'], 1).click() except AssertionError: pass # Вводим промокод BaseElement(self.locators['inpField']).set_text(promo_code) # Нажимаем применить промокод BaseElement(self.locators['txbApply']).click() FrontShopCartPage(is_wait=True)
def _error_check_message(self) -> None: """ Проверка промокода на наличие ошибок """ try: message = BaseElement(self.locators['txbError'], 0).text if message == "Can not apply.": self.errors.append(('promocode_cant_apply', True)) elif message == "123": self.errors.append(('promocode_not_exist', True)) else: self.errors.append(('promocode', message)) except AssertionError: pass
def _error_in_quantity(self, product_name: str, inp_quantity: BaseElement, count_message: str) -> None: """ Поле количество указанного товара """ error = 0 if "isError__" in inp_quantity.ge().get_attribute('class'): error += 1 if count_message: error += 1 if error == 2: self.errors.append(('product_quantity', product_name))
def __init__(self): total_price = convert_price_from_str_to_float(BaseElement(self.locators['txbTotalPrice'], 3).text) try: total_price_old = convert_price_from_str_to_float( BaseElement(self.locators['txbTotalPriceOld'], 0).text) except AssertionError: total_price_old = None amount_price = convert_price_from_str_to_float(BaseElement(self.locators['txbAmountPrice'], 3).text) try: amount_price_old = convert_price_from_str_to_float( BaseElement(self.locators['txbAmountPriceOld'], 0).text) except AssertionError: amount_price_old = None self.prices = { 'total_price': total_price, 'total_price_old': total_price_old, 'amount_price': amount_price, 'amount_price_old': amount_price_old, }
def _error_form_fields_are_red(self, names: list = None) -> None: """ Незаполненные формы должны подсветиться красным """ error_field_names = [] if not names: names = [item[0] for item in self.default_fields] for name in names: try: BaseElement(self.locators['inpFieldRed'].format(value=name), 0) except AssertionError: error_field_names.append(name) if error_field_names: self.errors.append(('form_fields_not_red', error_field_names))
def fill_form(self, fields: list) -> None: """ Заполняем форму заказа fields: [(name, value), (name, value), ...] """ names_without_check = [] names = [field[0] for field in fields] for default_field in self.default_fields: if default_field[0] not in names: fields.append(default_field) names_without_check.append(default_field[0]) for field in fields: try: BaseElement(self.locators['inpField'].format(value=field[0]), 0).set_text(field[1]) except AssertionError: assert field[0] not in names_without_check, f"Не удалось найти поле '{field[0]}' в форме"
def _error_check_under_form(self) -> None: """ Проверка формы на наличие ошибок """ try: BaseElement(self.locators['txbErrors'], 0) errors = self._browser.find_elements_by_xpath(self.locators['txbErrors']) for error in errors: if error.text == "Стоимость изенилась": self.errors.append(('amount_price_change', True)) elif error.text == "Такой доставки нет": self.errors.append(('delivery_method_unavailable', True)) elif error.text == "Такой платежки нет": self.errors.append(('payment_method_unavailable', True)) else: self.errors.append(('under_form', error.text)) except AssertionError: pass
def __init__(self, browser: webdriver): self._browser = browser self.payments = [] div_payments = self._browser.find_elements_by_xpath(self.locators['divPayments']) for div_payment in div_payments: if not div_payment.is_displayed: # Метод оплаты не отображается continue html = etree.parse(StringIO(div_payment.get_property('outerHTML')), parser=etree.HTMLParser()) # Описание try: description = html.xpath(self.locators['txbDescription'])[0].text except IndexError: description = None # Название метода name_element = BaseElement(self.locators['txbName'], context=div_payment) if description: name = re.search(r"(.+)\n", name_element.text).group(1) else: name = name_element.text # Комиссия price = html.xpath(self.locators['txbPrice'])[0].text if price: price = convert_price_from_str_to_float(price) else: price = None self.payments.append({ 'name': name, # название метода 'description': description, # описание 'price': price, # комиссия 'elements': { 'name': name_element # элемент названия метода } })
def _parse_page(self) -> dict: """ Распарсить страницу товара """ elements = {} # Определение контекста, для парсинга информации на странице div_description = BaseElement(self.locators['locDescription']) div_description_html = etree.parse(StringIO( div_description.ge().get_property('outerHTML')), parser=etree.HTMLParser()) # Название товара name = div_description_html.xpath( self.locators['txtName'])[0].text.strip() # Артикул vendor_code = None temp = div_description_html.xpath(self.locators['txtVendorCode']) if temp[0].text is not None: temp = temp[0].text.strip() if temp: vendor_code = { 'text': re.sub(r"\s+", " ", temp), 'code': re.search(r":\s*(.*)$", temp).group(1).rstrip() } # Цена и цена от.. price = None price_from = None temp = div_description_html.xpath(self.locators['txtPrice']) if temp[0].text is not None: if "от" in temp[0].text or (temp[0].getparent().text is not None and "от" in temp[0].getparent().text): price_from = convert_price_from_str_to_float( temp[0].text.replace("\xa0", " ")) else: price = convert_price_from_str_to_float(temp[0].text) # Цена со скидкой price_discount = None temp = div_description_html.xpath(self.locators['txtPriceDiscount']) if len(temp): temp = temp[0].text if temp: price_discount = convert_price_from_str_to_float(temp) # Параметры params = None div_params = div_description_html.xpath(self.locators['locParams']) if len(div_params): params = [] for div_param in div_params: # id параметра param_id = int( div_param.xpath( self.locators['locParamId'])[0].attrib['name']) # выбранное значнеие selected = div_param.xpath( self.locators['txtParamSelected'])[0].text # Значения values = [] div_values = div_param.xpath(self.locators['locParamValues']) for div_value in div_values: # Проверяем отображение значения параметра try: disabled = True if div_value.attrib[ 'disabled'] else False except KeyError: disabled = False values.append({ 'id': int(div_value.attrib['data-value']), # id значения 'name': div_value.text, # имя значения 'disabled': disabled, # отображается ли он }) params.append({ 'id': param_id, # id параметра 'selected': selected, # выбранное значение 'values': values, # словарь значений }) # Поле с количеством quantity = None temp = div_description_html.xpath(self.locators['inpQuantity']) if len(temp): quantity = int(temp[0].attrib['value']) # количество elements['quantity_minus'] = BaseElement( self.locators['btnQuantityMinus']) elements['quantity'] = BaseElement(self.locators['inpQuantity']) elements['quantity_plus'] = BaseElement( self.locators['btnQuantityPlus']) # Кнопка button = None temp = div_description_html.xpath(self.locators['btnAdd'] + "/span") if len(temp): button_text = temp[0].text.strip() if "Заказать" in button_text: button_type = 'preOrder' else: button_type = 'addToCard' button = { 'text': button_text, # текст внутри 'type': button_type, # тип } # Элемент кнопка elements['button'] = BaseElement(self.locators['btnAdd']) else: temp = div_description_html.xpath(self.locators['btnPreOrder'] + "/span") if len(temp): button = { 'text': temp[0].text.strip(), # текст внутри 'type': 'preOrder', # тип } # Элемент кнопка elements['button'] = BaseElement(self.locators['btnPreOrder']) # Количестве товара count = None temp = div_description_html.xpath(self.locators['locCount']) if len(temp): text = temp[0].xpath("string()").strip() if text: text = re.sub(r"\s+", " ", text) count = { 'text': text, # текст сообщения 'number': int( div_description_html.xpath(self.locators['locCount'] + "/span")[0].text), } result = { 'name': name, # название товара 'vendor_code': vendor_code, # артикул 'price': price, # цена 'price_from': price_from, # цена от.. 'price_discount': price_discount, # цена со скидкой 'params': params, # словарь параметров 'quantity': quantity, # количество 'button': button, # словарь кнопки 'count': count, # словарь количества товара в наличии 'elements': elements # словарь элементов } return result
def open_product_by_name(self, product_name: str) -> None: """ Открыть продукт по имени """ BaseElement(self.locators['productName'].format(product_name)).click()
def open_last_order(self) -> None: """ Открыть последний заказ """ self._browser.refresh() BaseElement(self.locators['txbLastOrder']).click()
def click_on_button_add_to_cart(self) -> None: """ Нажать на кнопку 'Добавить в корзину' """ BaseElement(self.locators['btnAddToCart']).click() time.sleep(2.5)
def click_on_button_save(self): """ Стандартная кнопка сохранения для магазина """ BaseElement(self.base_admin_shop_locators['btnSubmit']).click() time.sleep(1)
def get_order_number(self) -> str: """ Извлечение номера заказа """ txb_order_number = BaseElement(self.locators["txbOrderTitle"]).text return re.search(r"(\d{6})", txb_order_number).group(0)
def get_price_by_order_number(self, order_number: str) -> float: """ Получить сумму заказа по номеру заказа """ txb_price = BaseElement(self.locators['txbPriceByOrderNumber'].format(value=order_number)).text return convert_price_from_str_to_float(txb_price)
def login(self, name: str = None, password: str = None) -> None: """ Метод логина в админку сайта """ inp_login = BaseElement(self.locators['inpLogin']) name = name or TEST_SITE['login_name'] inp_login.set_text(name) inp_password = BaseElement(self.locators['inpPassword']) password = password or TEST_SITE['login_password'] inp_password.set_text(password) btn_login = BaseElement(self.locators['btnLogin']) btn_login.click()
def _error_min_order_message(self) -> None: try: self.errors.append(('min_order_message', BaseElement(self.locators['txbMinOrderMessage'], 0).text)) except AssertionError: pass
def _get_order(self) -> dict: order = {} div_order_data = BaseElement(self.locators['locOrderData']) html = etree.parse(StringIO( div_order_data.ge().get_property('outerHTML')), parser=etree.HTMLParser()) div_fields = html.xpath(self.locators['locOrderDataFields']) temp = div_fields[0].text order['number'] = re.search(r"(\d{6})", temp).group(0) temp = div_fields[1].text order['date'] = re.search(r"Дата:\s*(.*)$", temp).group(1) temp = div_fields[2].text order['type'] = re.search(r"Тип:\s*(.*)$", temp).group(1) order['status'] = div_fields[3].xpath( self.locators['btnOrderDataField'])[0].attrib['title'] order['delivery'] = div_fields[4].xpath( self.locators['btnOrderDataField'])[0].attrib['title'] order['payment'] = div_fields[5].xpath( self.locators['btnOrderDataField'])[0].attrib['title'] order['promocode'] = div_fields[-1].xpath( self.locators['btnOrderDataField'])[0].attrib['title'] if len(order['promocode']) != 3: order['promocode'] = re.search(r"^(.*)\s-\s.*$", order['promocode']).group(1) order['form'] = [] # for div_field in div_fields[6:-1]: # order['form'].append(div_field.xpath(self.locators['inpOrderDataFields'])[0].attrib['value']) order['products'] = [] order_table = BaseElement(self.locators['locOrderTable']) html = etree.parse(StringIO( order_table.ge().get_property('outerHTML')), parser=etree.HTMLParser()) tr_elements = html.xpath(".//tr")[:-5] for tr_element in tr_elements: vendor_code = tr_element.xpath( ".//span[@class='vendor-code']")[0].text order['products'].append({ 'name': tr_element.xpath(".//td[@class='sku']")[0].text + "|" + vendor_code, 'price_per_product': convert_price_from_str_to_float( tr_element.xpath(".//td[@class='sku-price']") [0].attrib['value']), 'quantity': int( tr_element.xpath(".//td[@class='sku-quantit']") [0].attrib['value']), 'price_amount': convert_price_from_str_to_float( tr_element.xpath(".//td[@class='total-cost']")[0].text), }) order['delivery_tax'] = convert_price_from_str_to_float( html.xpath(self.locators['inpDeliveryTax'])[0].attrib['value']) order['discount'] = convert_price_from_str_to_float( html.xpath(self.locators['inpDiscount'])[0].attrib['value']) order['markup'] = convert_price_from_str_to_float( html.xpath(self.locators['inpMarkup'])[0].attrib['value']) order['total_amount'] = convert_price_from_str_to_float( html.xpath(self.locators['txbTotalAmount'])[0].text) return order