From ea8be754b3256a6c18db6f1d45da560741374041 Mon Sep 17 00:00:00 2001 From: ilshat Date: Mon, 30 Dec 2024 15:19:03 +0300 Subject: [PATCH] task 5 --- .gitignore | 162 ++++++++++++++++++++++++++++++++ README.md | 5 + conftest.py | 9 ++ data.py | 14 +++ helpers.py | 6 ++ locators/__init__.py | 0 locators/header_locators.py | 7 ++ locators/main_page_locators.py | 14 +++ locators/order_page_locators.py | 20 ++++ pages/__init__.py | 0 pages/base_page.py | 31 ++++++ pages/main_page.py | 19 ++++ pages/order_page.py | 27 ++++++ requirements.txt | 23 +++++ tests/__init__.py | 0 tests/test_main_page.py | 52 ++++++++++ tests/test_order_page.py | 44 +++++++++ 17 files changed, 433 insertions(+) create mode 100644 README.md create mode 100644 conftest.py create mode 100644 data.py create mode 100644 helpers.py create mode 100644 locators/__init__.py create mode 100644 locators/header_locators.py create mode 100644 locators/main_page_locators.py create mode 100644 locators/order_page_locators.py create mode 100644 pages/__init__.py create mode 100644 pages/base_page.py create mode 100644 pages/main_page.py create mode 100644 pages/order_page.py create mode 100644 requirements.txt create mode 100644 tests/__init__.py create mode 100644 tests/test_main_page.py create mode 100644 tests/test_order_page.py diff --git a/.gitignore b/.gitignore index e69de29..287a2f0 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,162 @@ +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..efe02b1 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Sprint_6 +В файле test_main_page.py находятся тесты на проверку вопросов/ответов, а также проверки кликов по логотипам Яндекса +и самоката +В файле test_order_page.py тесты на проверку кнопок "Заказать" в шапке и в середине главной странице, а также тест на +весь флоу оформления заказа с двумя наборами данных \ No newline at end of file diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..4908d2d --- /dev/null +++ b/conftest.py @@ -0,0 +1,9 @@ +import pytest +from selenium import webdriver + + +@pytest.fixture +def driver(): + driver = webdriver.Firefox() + yield driver + driver.quit() \ No newline at end of file diff --git a/data.py b/data.py new file mode 100644 index 0000000..496f367 --- /dev/null +++ b/data.py @@ -0,0 +1,14 @@ +ANSWER_TEXTS = ['Сутки — 400 рублей. Оплата курьеру — наличными или картой.', + 'Пока что у нас так: один заказ — один самокат. Если хотите покататься с друзьями,' + ' можете просто сделать несколько заказов — один за другим.', + 'Допустим, вы оформляете заказ на 8 мая. Мы привозим самокат 8 мая в течение дня. ' + 'Отсчёт времени аренды начинается с момента, когда вы оплатите заказ курьеру. ' + 'Если мы привезли самокат 8 мая в 20:30, суточная аренда закончится 9 мая в 20:30.', + 'Только начиная с завтрашнего дня. Но скоро станем расторопнее.', + 'Пока что нет! Но если что-то срочное — всегда можно позвонить в поддержку по красивому номеру 1010.', + 'Самокат приезжает к вам с полной зарядкой. ' + 'Этого хватает на восемь суток — даже если будете кататься без передышек и во сне. ' + 'Зарядка не понадобится.', + 'Да, пока самокат не привезли. Штрафа не будет, объяснительной записки тоже не попросим. Все же свои.', + 'Да, обязательно. Всем самокатов! И Москве, и Московской области.'] + diff --git a/helpers.py b/helpers.py new file mode 100644 index 0000000..6beef57 --- /dev/null +++ b/helpers.py @@ -0,0 +1,6 @@ +import random + + +class Helpers: + def random_phone_number(self): + return random.randint(10000000000, 9999999999999) \ No newline at end of file diff --git a/locators/__init__.py b/locators/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locators/header_locators.py b/locators/header_locators.py new file mode 100644 index 0000000..bf77ffc --- /dev/null +++ b/locators/header_locators.py @@ -0,0 +1,7 @@ +from selenium.webdriver.common.by import By + + +class HeaderLocators: + SCOOTER_LOGO_BUTTON = By.XPATH, './/img[@alt ="Scooter"]' + YANDEX_LOGO_BUTTON = By.XPATH, './/img[@alt ="Yandex"]' + SEARCH_BUTTON_ON_DZEN_PAGE = By.XPATH, './/button[contains(text(),"Найти")]' diff --git a/locators/main_page_locators.py b/locators/main_page_locators.py new file mode 100644 index 0000000..050e629 --- /dev/null +++ b/locators/main_page_locators.py @@ -0,0 +1,14 @@ +from selenium.webdriver.common.by import By + + +class MainPageLocators: + + QUESTION_LOCATOR = By.XPATH, '//*[@id = "accordion__heading-{}"]' + ANSWER_LOCATOR = By.XPATH, '//*[@id="accordion__panel-{}"]' + QUESTION_LOCATOR_8 = By.XPATH, '//*[@id="accordion__heading-7"]' + COOKIE_LOCATOR = By.XPATH, '//*[@id="rcc-confirm-button"]' + UPPER_ORDER_BUTTON = By.CLASS_NAME, 'Button_Button__ra12g' + LOWER_ORDER_BUTTON = By.XPATH, './/button[@class="Button_Button__ra12g Button_Middle__1CSJM"]' + QUESTIONS_HEADER_LOCATOR = By.XPATH, './/div[contains(text(),"Вопросы о важном")]' + HOME_PAGE_HEADER = By.XPATH, './/div[@class="Home_Header__iJKdX"]' + diff --git a/locators/order_page_locators.py b/locators/order_page_locators.py new file mode 100644 index 0000000..ae4f12e --- /dev/null +++ b/locators/order_page_locators.py @@ -0,0 +1,20 @@ +from selenium.webdriver.common.by import By + + +class OrderPageLocators: + STATION_INPUT_LOCATOR = By.XPATH, './/input[@placeholder ="* Станция метро"]' + STATION_LOCATOR = By.XPATH, '//div[text()="Черкизовская" ]/parent::button' + NAME_INPUT_LOCATOR = By.XPATH, './/input[@placeholder ="* Имя"]' + LAST_NAME_INPUT_LOCATOR = By.XPATH, './/input[@placeholder ="* Фамилия"]' + ADRESS_INPUT_LOCATOR = By.XPATH, './/input[@placeholder ="* Адрес: куда привезти заказ"]' + PHONE_INPUT_LOCATOR = By.XPATH, './/input[@placeholder ="* Телефон: на него позвонит курьер"]' + NEXT_PAGE_BUTTON_LOCATOR = By.XPATH, './/button[contains(text(),"Далее")]' + DELIVERY_DAY_INPUT_LOCATOR = By.XPATH, './/input[@placeholder ="* Когда привезти самокат"]' + DURATION_CHOICE_LOCATOR = By.XPATH, './/span[@class="Dropdown-arrow"]' + ONE_DAY_LOCATOR = By.XPATH, './/div[contains(text(),"сутки")]' + COLOR_CHECKBOX_LOCATOR = By.ID, 'black' + ORDER_BUTTON_LOCATOR = By.XPATH, './/button[@class="Button_Button__ra12g Button_Middle__1CSJM"]' + CONFIRMATION_BUTTON_LOCATOR = By.XPATH, './/button[contains(text(),"Да")]' + ORDER_CONFIRMATION_TEXT_LOCATOR = By.CLASS_NAME, 'Order_ModalHeader__3FDaJ' + ORDER_PAGE_HEADER_LOCATOR = By.CLASS_NAME, 'Order_Header__BZXOb' + diff --git a/pages/__init__.py b/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pages/base_page.py b/pages/base_page.py new file mode 100644 index 0000000..e0ae00f --- /dev/null +++ b/pages/base_page.py @@ -0,0 +1,31 @@ +from selenium.webdriver.support import expected_conditions +from selenium.webdriver.support.wait import WebDriverWait + + +class BasePage: + + def __init__(self, driver): + self.driver = driver + + def find_element_with_wait(self, locator): + WebDriverWait(self.driver, 5).until(expected_conditions.visibility_of_element_located(locator)) + return self.driver.find_element(*locator) + + def click_to_element(self, locator): + WebDriverWait(self.driver, 5).until(expected_conditions.element_to_be_clickable(locator)) + self.driver.find_element(*locator).click() + + def add_text_to_element(self, locator, text): + self.find_element_with_wait(locator).send_keys(text) + + def get_text_from_element(self, locator): + return self.find_element_with_wait(locator).text + + def scroll_to_element(self, locator): + element = self.find_element_with_wait(locator) + self.driver.execute_script("arguments[0].scrollIntoView();", element) + + def format_locators(self, locator_1, num): + method, locator = locator_1 + locator = locator.format(num) + return method, locator diff --git a/pages/main_page.py b/pages/main_page.py new file mode 100644 index 0000000..0365869 --- /dev/null +++ b/pages/main_page.py @@ -0,0 +1,19 @@ +from pages.base_page import BasePage +from locators.main_page_locators import (MainPageLocators) +import allure + + +class MainPage(BasePage): + + @allure.step('Получаем текст с ответа') + def get_answer_text(self, num): + locator_q_formatted = self.format_locators(MainPageLocators.QUESTION_LOCATOR, num) + locator_a_formatted = self.format_locators(MainPageLocators.ANSWER_LOCATOR, num) + self.scroll_to_element(MainPageLocators.QUESTION_LOCATOR_8) + self.find_element_with_wait(locator_q_formatted) + self.click_to_element(locator_q_formatted) + self.find_element_with_wait(locator_a_formatted) + return self.get_text_from_element(locator_a_formatted) + + + diff --git a/pages/order_page.py b/pages/order_page.py new file mode 100644 index 0000000..7d92493 --- /dev/null +++ b/pages/order_page.py @@ -0,0 +1,27 @@ +from pages.base_page import BasePage +from locators.order_page_locators import (OrderPageLocators) +import allure +from helpers import Helpers + + +class OrderPage(BasePage): + + @allure.step('Создаем заказ') + def set_order(self, name, last_name, address, day): + self.click_to_element(OrderPageLocators.STATION_INPUT_LOCATOR) + self.click_to_element(OrderPageLocators.STATION_LOCATOR) + self.add_text_to_element(OrderPageLocators.NAME_INPUT_LOCATOR, name) + self.add_text_to_element(OrderPageLocators.LAST_NAME_INPUT_LOCATOR, last_name) + self.add_text_to_element(OrderPageLocators.ADRESS_INPUT_LOCATOR, address) + self.add_text_to_element(OrderPageLocators.PHONE_INPUT_LOCATOR, Helpers.random_phone_number(self)) + self.click_to_element(OrderPageLocators.NEXT_PAGE_BUTTON_LOCATOR) + self.add_text_to_element(OrderPageLocators.DELIVERY_DAY_INPUT_LOCATOR, day) + self.click_to_element(OrderPageLocators.DURATION_CHOICE_LOCATOR) + self.click_to_element(OrderPageLocators.ONE_DAY_LOCATOR) + self.click_to_element(OrderPageLocators.COLOR_CHECKBOX_LOCATOR) + self.click_to_element(OrderPageLocators.ORDER_BUTTON_LOCATOR) + self.click_to_element(OrderPageLocators.CONFIRMATION_BUTTON_LOCATOR) + + @allure.step('Проверяем, что заказ создался') + def check_order(self): + return self.get_text_from_element(OrderPageLocators.ORDER_CONFIRMATION_TEXT_LOCATOR) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..203ea03 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,23 @@ +allure-pytest==2.13.5 +allure-python-commons==2.13.5 +attrs==24.2.0 +certifi==2024.8.30 +exceptiongroup==1.2.2 +h11==0.14.0 +idna==3.10 +iniconfig==2.0.0 +outcome==1.3.0.post0 +packaging==24.1 +pluggy==1.5.0 +PySocks==1.7.1 +pytest==8.3.3 +selenium==4.25.0 +sniffio==1.3.1 +sortedcontainers==2.4.0 +tomli==2.0.2 +trio==0.26.2 +trio-websocket==0.11.1 +typing_extensions==4.12.2 +urllib3==2.2.3 +websocket-client==1.8.0 +wsproto==1.2.0 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_main_page.py b/tests/test_main_page.py new file mode 100644 index 0000000..e435424 --- /dev/null +++ b/tests/test_main_page.py @@ -0,0 +1,52 @@ +import pytest +from data import ANSWER_TEXTS +from pages.main_page import MainPage +from pages.order_page import OrderPage +import allure +from locators.main_page_locators import MainPageLocators +from locators.header_locators import HeaderLocators +from selenium.webdriver.support.wait import WebDriverWait +from selenium.webdriver.support import expected_conditions + + +class TestMainPage: + @allure.description('Проверяем, что при клике на вопрос ответ правильный') + @pytest.mark.parametrize( + 'num, result', + [ + (0, ANSWER_TEXTS[0]), + (1, ANSWER_TEXTS[1]), + (2, ANSWER_TEXTS[2]), + (3, ANSWER_TEXTS[3]), + (4, ANSWER_TEXTS[4]), + (5, ANSWER_TEXTS[5]), + (6, ANSWER_TEXTS[6]), + (7, ANSWER_TEXTS[7]), + ] + ) + def test_questions_and_answers(self, driver, num, result): + main_page = MainPage(driver) + driver.get('https://qa-scooter.praktikum-services.ru/') + main_page.click_to_element(MainPageLocators.COOKIE_LOCATOR) + assert main_page.get_answer_text(num) == result + + @allure.description('Проверяем, что при клике на логотип "Самоката" открывается главная страница') + def test_scooter_logo_click(self, driver): + main_page = MainPage(driver) + order_page = OrderPage(driver) + driver.get('https://qa-scooter.praktikum-services.ru/') + main_page.click_to_element(MainPageLocators.UPPER_ORDER_BUTTON) + order_page.click_to_element(HeaderLocators.SCOOTER_LOGO_BUTTON) + assert 'на пару дней' in main_page.get_text_from_element(MainPageLocators.HOME_PAGE_HEADER) + + @allure.description('Проверяем, что при клике на логотип яндекса происходит редирект и открывается главная страница' + ' Дзена.') + def test_yandex_logo_click(self, driver): + main_page = MainPage(driver) + driver.get('https://qa-scooter.praktikum-services.ru/') + main_page.click_to_element(HeaderLocators.YANDEX_LOGO_BUTTON) + handles = driver.window_handles + driver.switch_to.window(handles[1]) + WebDriverWait(driver, 5).until(expected_conditions.visibility_of_element_located(HeaderLocators. + SEARCH_BUTTON_ON_DZEN_PAGE)) + assert 'Найти' in driver.find_element(*HeaderLocators.SEARCH_BUTTON_ON_DZEN_PAGE).text diff --git a/tests/test_order_page.py b/tests/test_order_page.py new file mode 100644 index 0000000..2df5ea1 --- /dev/null +++ b/tests/test_order_page.py @@ -0,0 +1,44 @@ +import pytest +from pages.main_page import MainPage +from pages.order_page import OrderPage +import allure +from locators.main_page_locators import MainPageLocators +from locators.order_page_locators import OrderPageLocators + + +class TestOrderPage: + @allure.description('Проверяем, что при клике на верхнюю кнопку "заказать" открывается страница заказа') + def test_upper_order_button(self, driver): + main_page = MainPage(driver) + order_page = OrderPage(driver) + driver.get('https://qa-scooter.praktikum-services.ru/') + main_page.click_to_element(MainPageLocators.UPPER_ORDER_BUTTON) + assert order_page.get_text_from_element(OrderPageLocators.ORDER_PAGE_HEADER_LOCATOR) == 'Для кого самокат' + + @allure.description('Проверяем, что при клике на нижнюю кнопку "заказать" открывается страница заказа') + def test_lower_order_button(self, driver): + main_page = MainPage(driver) + order_page = OrderPage(driver) + driver.get('https://qa-scooter.praktikum-services.ru/') + main_page.click_to_element(MainPageLocators.COOKIE_LOCATOR) + main_page.scroll_to_element(MainPageLocators.QUESTIONS_HEADER_LOCATOR) + main_page.find_element_with_wait(MainPageLocators.LOWER_ORDER_BUTTON) + main_page.click_to_element(MainPageLocators.LOWER_ORDER_BUTTON) + assert order_page.get_text_from_element(OrderPageLocators.ORDER_PAGE_HEADER_LOCATOR) == 'Для кого самокат' + + @allure.description('Тестируем весь флоу оформления заказа и убеждаемся что он оформлен') + @pytest.mark.parametrize( + 'name, last_name, address, day', + [ + ('Петя', 'Иванов', 'Москва', '11.11.2024'), + ('Вася', 'Сидоров', 'Химки', '14.11.2024') + ] + ) + def test_successful_order(self, driver, name, last_name, address, day): + order_page = OrderPage(driver) + main_page = MainPage(driver) + driver.get('https://qa-scooter.praktikum-services.ru/') + main_page.click_to_element(MainPageLocators.COOKIE_LOCATOR) + main_page.click_to_element(MainPageLocators.UPPER_ORDER_BUTTON) + order_page.set_order(name, last_name, address, day) + assert 'Заказ оформлен' in order_page.check_order()