From f1b99e6658dc6899ec502cc66fcc83e5b1aca7cd Mon Sep 17 00:00:00 2001 From: Anastasia Nogovitcina Date: Mon, 23 Mar 2026 08:40:34 +0100 Subject: [PATCH] part_1 --- .gitignore | 32 ++++++++ conftest.py | 9 +++ helpers.py | 20 +++++ locators/__init__.py | 0 locators/locators.py | 81 +++++++++++++++++++ praktikum/__init__.py | 0 praktikum/bun.py | 10 +++ praktikum/burger.py | 34 ++++++++ praktikum/database.py | 27 +++++++ praktikum/ingredient.py | 14 ++++ praktikum/ingredient_types.py | 2 + pytest.ini | 2 + requirements.txt | 2 + tests/__init__.py | 0 tests/test_bun.py | 23 ++++++ tests/test_burger.py | 142 +++++++++++++++++++++++++++++++++ tests/test_constructor.py | 17 ++++ tests/test_database.py | 32 ++++++++ tests/test_ingredient.py | 32 ++++++++ tests/test_login.py | 93 +++++++++++++++++++++ tests/test_personal_account.py | 87 ++++++++++++++++++++ tests/test_registration.py | 40 ++++++++++ 22 files changed, 699 insertions(+) create mode 100644 .gitignore create mode 100644 conftest.py create mode 100644 helpers.py create mode 100644 locators/__init__.py create mode 100644 locators/locators.py create mode 100644 praktikum/__init__.py create mode 100644 praktikum/bun.py create mode 100644 praktikum/burger.py create mode 100644 praktikum/database.py create mode 100644 praktikum/ingredient.py create mode 100644 praktikum/ingredient_types.py create mode 100644 pytest.ini create mode 100644 requirements.txt create mode 100644 tests/__init__.py create mode 100644 tests/test_bun.py create mode 100644 tests/test_burger.py create mode 100644 tests/test_constructor.py create mode 100644 tests/test_database.py create mode 100644 tests/test_ingredient.py create mode 100644 tests/test_login.py create mode 100644 tests/test_personal_account.py create mode 100644 tests/test_registration.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..0d7af75bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Python +__pycache__/ +*.py[cod] +*.pyo +.pytest_cache/ +*.egg-info/ +dist/ +build/ +.eggs/ + +# Virtual environment +venv/ +.venv/ +env/ + +# IDE +.idea/ +.vscode/ +*.iml + +# OS +.DS_Store +Thumbs.db + +# Logs and reports +*.log +reports/ +allure-results/ + +# Selenium / browser +chromedriver +geckodriver diff --git a/conftest.py b/conftest.py new file mode 100644 index 000000000..85f8d355d --- /dev/null +++ b/conftest.py @@ -0,0 +1,9 @@ +import pytest +from selenium import webdriver + + +@pytest.fixture +def driver(): + driver = webdriver.Chrome() + yield driver + driver.quit() \ No newline at end of file diff --git a/helpers.py b/helpers.py new file mode 100644 index 000000000..a9a87f0e3 --- /dev/null +++ b/helpers.py @@ -0,0 +1,20 @@ +import random +import string + +BASE_URL = "https://stellarburgers.education-services.ru" +LOGIN_URL = f"{BASE_URL}/login" +REGISTER_URL = f"{BASE_URL}/register" +FORGOT_PASSWORD_URL = f"{BASE_URL}/forgot-password" +PROFILE_URL = f"{BASE_URL}/account/profile" + + +def generate_email(): + """Генерирует уникальный email в формате имя_фамилия_когорта_XXX@домен.""" + digits = "".join(random.choices(string.digits, k=6)) + return f"test_testov_999_{digits}@yandex.ru" + + +def generate_password(length=8): + """Генерирует случайный пароль заданной длины (минимум 6 символов).""" + chars = string.ascii_letters + string.digits + return "".join(random.choices(chars, k=length)) diff --git a/locators/__init__.py b/locators/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/locators/locators.py b/locators/locators.py new file mode 100644 index 000000000..fb1333969 --- /dev/null +++ b/locators/locators.py @@ -0,0 +1,81 @@ +from selenium.webdriver.common.by import By + + +class MainPageLocators: + # Кнопка «Войти в аккаунт» в конструкторе (видна только неавторизованным) + LOGIN_BUTTON = (By.XPATH, "//button[text()='Войти в аккаунт']") + + # Ссылка «Личный Кабинет» в шапке (тег ) + PERSONAL_ACCOUNT_LINK = (By.XPATH, "//a[@href='/account']") + + # Ссылка «Конструктор» в шапке (тег ) + CONSTRUCTOR_LINK = (By.XPATH, "//a[@href='/'][.//p[contains(@class,'AppHeader_header__linkText')]]") + + # Логотип Stellar Burgers в шапке (div-обёртка с классом header__logo) + LOGO = (By.XPATH, "//div[contains(@class,'AppHeader_header__logo')]") + + # Вкладка «Булки» в конструкторе (кликабельный div с текстом внутри span) + BUNS_TAB = (By.XPATH, "//div[contains(@class,'tab_tab')]//span[text()='Булки']") + + # Вкладка «Соусы» в конструкторе + SAUCES_TAB = (By.XPATH, "//div[contains(@class,'tab_tab')]//span[text()='Соусы']") + + # Вкладка «Начинки» в конструкторе + FILLINGS_TAB = (By.XPATH, "//div[contains(@class,'tab_tab')]//span[text()='Начинки']") + + # Заголовок раздела «Булки» в списке ингредиентов (h2) + BUNS_HEADING = (By.XPATH, "//h2[text()='Булки']") + + # Заголовок раздела «Соусы» в списке ингредиентов (h2) + SAUCES_HEADING = (By.XPATH, "//h2[text()='Соусы']") + + # Заголовок раздела «Начинки» в списке ингредиентов (h2) + FILLINGS_HEADING = (By.XPATH, "//h2[text()='Начинки']") + + +class LoginPageLocators: + # Поле ввода email на странице входа (первый fieldset, оба поля имеют name="name") + EMAIL_INPUT = (By.XPATH, "//fieldset[1]//input") + + # Поле ввода пароля на странице входа (второй fieldset, name="Пароль") + PASSWORD_INPUT = (By.XPATH, "//input[@name='Пароль']") + + # Кнопка «Войти» на странице входа + LOGIN_BUTTON = (By.XPATH, "//button[text()='Войти']") + + # Ссылка «Зарегистрироваться» на странице входа (класс Auth_link) + REGISTER_LINK = (By.XPATH, "//a[@href='/register']") + + # Ссылка «Восстановить пароль» на странице входа + FORGOT_PASSWORD_LINK = (By.XPATH, "//a[@href='/forgot-password']") + + +class RegisterPageLocators: + # Поле «Имя» на странице регистрации (первый fieldset) + NAME_INPUT = (By.XPATH, "//fieldset[1]//input") + + # Поле «Email» на странице регистрации (второй fieldset) + EMAIL_INPUT = (By.XPATH, "//fieldset[2]//input") + + # Поле «Пароль» на странице регистрации (третий fieldset) + PASSWORD_INPUT = (By.XPATH, "//fieldset[3]//input") + + # Кнопка «Зарегистрироваться» + REGISTER_BUTTON = (By.XPATH, "//button[text()='Зарегистрироваться']") + + # Ссылка «Войти» на странице регистрации (ведёт на /login) + LOGIN_LINK = (By.XPATH, "//a[@href='/login']") + + # Сообщение об ошибке при некорректном пароле + PASSWORD_ERROR = (By.XPATH, "//p[text()='Некорректный пароль']") + + +class ForgotPasswordPageLocators: + # Ссылка «Войти» на странице восстановления пароля (ведёт на /login) + LOGIN_LINK = (By.XPATH, "//a[@href='/login']") + + +class ProfilePageLocators: + # Кнопка «Выйти» в боковом меню личного кабинета + LOGOUT_BUTTON = (By.XPATH, "//button[text()='Выход']") + diff --git a/praktikum/__init__.py b/praktikum/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/praktikum/bun.py b/praktikum/bun.py new file mode 100644 index 000000000..d0a826c2d --- /dev/null +++ b/praktikum/bun.py @@ -0,0 +1,10 @@ +class Bun: + def __init__(self, name: str, price: float): + self.name = name + self.price = price + + def get_name(self) -> str: + return self.name + + def get_price(self) -> float: + return self.price diff --git a/praktikum/burger.py b/praktikum/burger.py new file mode 100644 index 000000000..4e28963fc --- /dev/null +++ b/praktikum/burger.py @@ -0,0 +1,34 @@ +from typing import List +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient + +class Burger: + def __init__(self): + self.bun = None + self.ingredients: List[Ingredient] = [] + + def set_buns(self, bun: Bun): + self.bun = bun + + def add_ingredient(self, ingredient: Ingredient): + self.ingredients.append(ingredient) + + def remove_ingredient(self, index: int): + del self.ingredients[index] + + def move_ingredient(self, index: int, new_index: int): + self.ingredients.insert(new_index, self.ingredients.pop(index)) + + def get_price(self) -> float: + price = self.bun.get_price() * 2 + for ingredient in self.ingredients: + price += ingredient.get_price() + return price + + def get_receipt(self) -> str: + receipt: List[str] = [f'(==== {self.bun.get_name()} ====)'] + for ingredient in self.ingredients: + receipt.append(f'= {str(ingredient.get_type()).lower()} {ingredient.get_name()} =') + receipt.append(f'(==== {self.bun.get_name()} ====)\n') + receipt.append(f'Price: {self.get_price()}') + return '\n'.join(receipt) diff --git a/praktikum/database.py b/praktikum/database.py new file mode 100644 index 000000000..17a244243 --- /dev/null +++ b/praktikum/database.py @@ -0,0 +1,27 @@ +from typing import List +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + +class Database: + def __init__(self): + self.buns: List[Bun] = [] + self.ingredients: List[Ingredient] = [] + + self.buns.append(Bun("black bun", 100)) + self.buns.append(Bun("white bun", 200)) + self.buns.append(Bun("red bun", 300)) + + self.ingredients.append(Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100)) + self.ingredients.append(Ingredient(INGREDIENT_TYPE_SAUCE, "sour cream", 200)) + self.ingredients.append(Ingredient(INGREDIENT_TYPE_SAUCE, "chili sauce", 300)) + + self.ingredients.append(Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 100)) + self.ingredients.append(Ingredient(INGREDIENT_TYPE_FILLING, "dinosaur", 200)) + self.ingredients.append(Ingredient(INGREDIENT_TYPE_FILLING, "sausage", 300)) + + def available_buns(self) -> List[Bun]: + return self.buns + + def available_ingredients(self) -> List[Ingredient]: + return self.ingredients diff --git a/praktikum/ingredient.py b/praktikum/ingredient.py new file mode 100644 index 000000000..0a8350eae --- /dev/null +++ b/praktikum/ingredient.py @@ -0,0 +1,14 @@ +class Ingredient: + def __init__(self, ingredient_type: str, name: str, price: float): + self.type = ingredient_type + self.name = name + self.price = price + + def get_price(self) -> float: + return self.price + + def get_name(self) -> str: + return self.name + + def get_type(self) -> str: + return self.type diff --git a/praktikum/ingredient_types.py b/praktikum/ingredient_types.py new file mode 100644 index 000000000..84e983fff --- /dev/null +++ b/praktikum/ingredient_types.py @@ -0,0 +1,2 @@ +INGREDIENT_TYPE_SAUCE = 'SAUCE' +INGREDIENT_TYPE_FILLING = 'FILLING' diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 000000000..a635c5c03 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +pythonpath = . diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..78b9d1e15 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pytest==8.2.0 +pytest-cov==5.0.0 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_bun.py b/tests/test_bun.py new file mode 100644 index 000000000..db4372a31 --- /dev/null +++ b/tests/test_bun.py @@ -0,0 +1,23 @@ +import pytest +from praktikum.bun import Bun + + +class TestBun: + + @pytest.mark.parametrize('name, price', [ + ('black bun', 100), + ('white bun', 200.5), + ('red bun', 0), + ]) + def test_get_name(self, name, price): + bun = Bun(name, price) + assert bun.get_name() == name + + @pytest.mark.parametrize('name, price', [ + ('black bun', 100), + ('white bun', 200.5), + ('red bun', 0), + ]) + def test_get_price(self, name, price): + bun = Bun(name, price) + assert bun.get_price() == price diff --git a/tests/test_burger.py b/tests/test_burger.py new file mode 100644 index 000000000..c1bf2afd9 --- /dev/null +++ b/tests/test_burger.py @@ -0,0 +1,142 @@ +import pytest +from unittest.mock import MagicMock +from praktikum.burger import Burger +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +@pytest.fixture +def mock_bun(): + bun = MagicMock(spec=Bun) + bun.get_name.return_value = 'black bun' + bun.get_price.return_value = 100 + return bun + + +@pytest.fixture +def mock_sauce(): + ingredient = MagicMock(spec=Ingredient) + ingredient.get_name.return_value = 'hot sauce' + ingredient.get_price.return_value = 50 + ingredient.get_type.return_value = INGREDIENT_TYPE_SAUCE + return ingredient + + +@pytest.fixture +def mock_filling(): + ingredient = MagicMock(spec=Ingredient) + ingredient.get_name.return_value = 'cutlet' + ingredient.get_price.return_value = 100 + ingredient.get_type.return_value = INGREDIENT_TYPE_FILLING + return ingredient + + +@pytest.fixture +def burger_with_bun(mock_bun): + burger = Burger() + burger.set_buns(mock_bun) + return burger + + +class TestBurgerSetBuns: + + def test_set_buns(self, mock_bun): + burger = Burger() + burger.set_buns(mock_bun) + assert burger.bun == mock_bun + + +class TestBurgerAddIngredient: + + def test_add_ingredient(self, burger_with_bun, mock_sauce): + burger_with_bun.add_ingredient(mock_sauce) + assert mock_sauce in burger_with_bun.ingredients + + def test_add_multiple_ingredients(self, burger_with_bun, mock_sauce, mock_filling): + burger_with_bun.add_ingredient(mock_sauce) + burger_with_bun.add_ingredient(mock_filling) + assert len(burger_with_bun.ingredients) == 2 + + +class TestBurgerRemoveIngredient: + + def test_remove_ingredient(self, burger_with_bun, mock_sauce, mock_filling): + burger_with_bun.add_ingredient(mock_sauce) + burger_with_bun.add_ingredient(mock_filling) + burger_with_bun.remove_ingredient(0) + assert mock_sauce not in burger_with_bun.ingredients + + def test_remove_ingredient_reduces_count(self, burger_with_bun, mock_sauce): + burger_with_bun.add_ingredient(mock_sauce) + burger_with_bun.remove_ingredient(0) + assert len(burger_with_bun.ingredients) == 0 + + +class TestBurgerMoveIngredient: + + def test_move_ingredient(self, burger_with_bun, mock_sauce, mock_filling): + burger_with_bun.add_ingredient(mock_sauce) + burger_with_bun.add_ingredient(mock_filling) + burger_with_bun.move_ingredient(1, 0) + assert burger_with_bun.ingredients[0] == mock_filling + assert burger_with_bun.ingredients[1] == mock_sauce + + +class TestBurgerGetPrice: + + def test_get_price_bun_only(self, burger_with_bun): + assert burger_with_bun.get_price() == 200 + + def test_get_price_with_ingredients(self, burger_with_bun, mock_sauce, mock_filling): + burger_with_bun.add_ingredient(mock_sauce) + burger_with_bun.add_ingredient(mock_filling) + assert burger_with_bun.get_price() == 350 + + @pytest.mark.parametrize('bun_price, ingredient_prices, expected', [ + (100, [], 200), + (100, [50], 250), + (200, [100, 150], 650), + ]) + def test_get_price_parametrized(self, bun_price, ingredient_prices, expected): + bun = MagicMock(spec=Bun) + bun.get_price.return_value = bun_price + bun.get_name.return_value = 'bun' + + burger = Burger() + burger.set_buns(bun) + + for price in ingredient_prices: + ingredient = MagicMock(spec=Ingredient) + ingredient.get_price.return_value = price + ingredient.get_name.return_value = 'ing' + ingredient.get_type.return_value = INGREDIENT_TYPE_SAUCE + burger.add_ingredient(ingredient) + + assert burger.get_price() == expected + + +class TestBurgerGetReceipt: + + def test_get_receipt_contains_bun_name(self, burger_with_bun): + assert 'black bun' in burger_with_bun.get_receipt() + + def test_get_receipt_contains_price(self, burger_with_bun): + assert 'Price: 200' in burger_with_bun.get_receipt() + + def test_get_receipt_contains_ingredient_name(self, burger_with_bun, mock_sauce): + burger_with_bun.add_ingredient(mock_sauce) + assert 'hot sauce' in burger_with_bun.get_receipt() + + def test_get_receipt_contains_ingredient_type(self, burger_with_bun, mock_sauce): + burger_with_bun.add_ingredient(mock_sauce) + assert 'sauce' in burger_with_bun.get_receipt() + + def test_get_receipt_format(self, burger_with_bun, mock_sauce): + burger_with_bun.add_ingredient(mock_sauce) + receipt = burger_with_bun.get_receipt() + + lines = receipt.split('\n') + + assert lines[0] == '(==== black bun ====)' + assert '(==== black bun ====)' in lines diff --git a/tests/test_constructor.py b/tests/test_constructor.py new file mode 100644 index 000000000..6a8648735 --- /dev/null +++ b/tests/test_constructor.py @@ -0,0 +1,17 @@ +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +from helpers import BASE_URL +from locators.locators import MainPageLocators + + +def test_navigate_to_buns(driver): + wait = WebDriverWait(driver, 5) + + driver.get(BASE_URL) + + heading = wait.until( + EC.visibility_of_element_located(MainPageLocators.BUNS_HEADING) + ) + + assert heading.is_displayed() \ No newline at end of file diff --git a/tests/test_database.py b/tests/test_database.py new file mode 100644 index 000000000..8c52e4565 --- /dev/null +++ b/tests/test_database.py @@ -0,0 +1,32 @@ +from praktikum.database import Database +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient + + +class TestDatabase: + + def test_available_buns_returns_list(self): + db = Database() + assert isinstance(db.available_buns(), list) + + def test_available_buns_not_empty(self): + db = Database() + assert len(db.available_buns()) > 0 + + def test_available_buns_contains_bun_instances(self): + db = Database() + for bun in db.available_buns(): + assert isinstance(bun, Bun) + + def test_available_ingredients_returns_list(self): + db = Database() + assert isinstance(db.available_ingredients(), list) + + def test_available_ingredients_not_empty(self): + db = Database() + assert len(db.available_ingredients()) > 0 + + def test_available_ingredients_contains_ingredient_instances(self): + db = Database() + for ingredient in db.available_ingredients(): + assert isinstance(ingredient, Ingredient) diff --git a/tests/test_ingredient.py b/tests/test_ingredient.py new file mode 100644 index 000000000..cb8ebc9c5 --- /dev/null +++ b/tests/test_ingredient.py @@ -0,0 +1,32 @@ +import pytest +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +class TestIngredient: + + @pytest.mark.parametrize('ingredient_type, name, price', [ + (INGREDIENT_TYPE_SAUCE, 'hot sauce', 100), + (INGREDIENT_TYPE_FILLING, 'cutlet', 200), + (INGREDIENT_TYPE_SAUCE, 'sour cream', 50.5), + ]) + def test_get_name(self, ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + assert ingredient.get_name() == name + + @pytest.mark.parametrize('ingredient_type, name, price', [ + (INGREDIENT_TYPE_SAUCE, 'hot sauce', 100), + (INGREDIENT_TYPE_FILLING, 'cutlet', 200), + (INGREDIENT_TYPE_SAUCE, 'sour cream', 50.5), + ]) + def test_get_price(self, ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + assert ingredient.get_price() == price + + @pytest.mark.parametrize('ingredient_type, name, price', [ + (INGREDIENT_TYPE_SAUCE, 'hot sauce', 100), + (INGREDIENT_TYPE_FILLING, 'cutlet', 200), + ]) + def test_get_type(self, ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + assert ingredient.get_type() == ingredient_type diff --git a/tests/test_login.py b/tests/test_login.py new file mode 100644 index 000000000..1ae2c919a --- /dev/null +++ b/tests/test_login.py @@ -0,0 +1,93 @@ +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +from helpers import BASE_URL, LOGIN_URL, REGISTER_URL, FORGOT_PASSWORD_URL, generate_email, generate_password +from locators.locators import MainPageLocators, LoginPageLocators, RegisterPageLocators, ForgotPasswordPageLocators + + +def register_and_get_credentials(driver): + wait = WebDriverWait(driver, 5) + + driver.get(REGISTER_URL) + + email = generate_email() + password = generate_password() + + wait.until(EC.element_to_be_clickable(RegisterPageLocators.NAME_INPUT)).send_keys("Test User") + driver.find_element(*RegisterPageLocators.EMAIL_INPUT).send_keys(email) + driver.find_element(*RegisterPageLocators.PASSWORD_INPUT).send_keys(password) + driver.find_element(*RegisterPageLocators.REGISTER_BUTTON).click() + + wait.until(EC.url_to_be(LOGIN_URL)) + + return email, password + + +class TestLogin: + + def test_login_via_main_button(self, driver): + wait = WebDriverWait(driver, 5) + + email, password = register_and_get_credentials(driver) + + driver.get(BASE_URL) + + wait.until(EC.element_to_be_clickable(MainPageLocators.LOGIN_BUTTON)).click() + + wait.until(EC.visibility_of_element_located(LoginPageLocators.EMAIL_INPUT)).send_keys(email) + driver.find_element(*LoginPageLocators.PASSWORD_INPUT).send_keys(password) + driver.find_element(*LoginPageLocators.LOGIN_BUTTON).click() + + wait.until(EC.url_to_be(BASE_URL + "/")) + assert driver.current_url == BASE_URL + "/" + + def test_login_via_personal_account_link(self, driver): + wait = WebDriverWait(driver, 5) + + email, password = register_and_get_credentials(driver) + + driver.get(BASE_URL) + + wait.until(EC.element_to_be_clickable(MainPageLocators.PERSONAL_ACCOUNT_LINK)).click() + wait.until(EC.url_to_be(LOGIN_URL)) + + wait.until(EC.visibility_of_element_located(LoginPageLocators.EMAIL_INPUT)).send_keys(email) + driver.find_element(*LoginPageLocators.PASSWORD_INPUT).send_keys(password) + driver.find_element(*LoginPageLocators.LOGIN_BUTTON).click() + + wait.until(EC.url_to_be(BASE_URL + "/")) + assert driver.current_url == BASE_URL + "/" + + def test_login_via_register_page_link(self, driver): + wait = WebDriverWait(driver, 5) + + email, password = register_and_get_credentials(driver) + + driver.get(REGISTER_URL) + + wait.until(EC.element_to_be_clickable(RegisterPageLocators.LOGIN_LINK)).click() + wait.until(EC.url_to_be(LOGIN_URL)) + + wait.until(EC.visibility_of_element_located(LoginPageLocators.EMAIL_INPUT)).send_keys(email) + driver.find_element(*LoginPageLocators.PASSWORD_INPUT).send_keys(password) + driver.find_element(*LoginPageLocators.LOGIN_BUTTON).click() + + wait.until(EC.url_to_be(BASE_URL + "/")) + assert driver.current_url == BASE_URL + "/" + + def test_login_via_forgot_password_link(self, driver): + wait = WebDriverWait(driver, 5) + + email, password = register_and_get_credentials(driver) + + driver.get(FORGOT_PASSWORD_URL) + + wait.until(EC.element_to_be_clickable(ForgotPasswordPageLocators.LOGIN_LINK)).click() + wait.until(EC.url_to_be(LOGIN_URL)) + + wait.until(EC.visibility_of_element_located(LoginPageLocators.EMAIL_INPUT)).send_keys(email) + driver.find_element(*LoginPageLocators.PASSWORD_INPUT).send_keys(password) + driver.find_element(*LoginPageLocators.LOGIN_BUTTON).click() + + wait.until(EC.url_to_be(BASE_URL + "/")) + assert driver.current_url == BASE_URL + "/" \ No newline at end of file diff --git a/tests/test_personal_account.py b/tests/test_personal_account.py new file mode 100644 index 000000000..3b2ae01a3 --- /dev/null +++ b/tests/test_personal_account.py @@ -0,0 +1,87 @@ +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +from helpers import BASE_URL, LOGIN_URL, REGISTER_URL, generate_email, generate_password +from locators.locators import MainPageLocators, LoginPageLocators, RegisterPageLocators, ProfilePageLocators + + +def register_and_login(driver): + wait = WebDriverWait(driver, 7) + + email = generate_email() + password = generate_password() + + driver.get(REGISTER_URL) + + wait.until(EC.visibility_of_element_located(RegisterPageLocators.NAME_INPUT)).send_keys("Test User") + driver.find_element(*RegisterPageLocators.EMAIL_INPUT).send_keys(email) + driver.find_element(*RegisterPageLocators.PASSWORD_INPUT).send_keys(password) + driver.find_element(*RegisterPageLocators.REGISTER_BUTTON).click() + + wait.until(EC.url_to_be(LOGIN_URL)) + + wait.until(EC.visibility_of_element_located(LoginPageLocators.EMAIL_INPUT)).send_keys(email) + driver.find_element(*LoginPageLocators.PASSWORD_INPUT).send_keys(password) + driver.find_element(*LoginPageLocators.LOGIN_BUTTON).click() + + wait.until(EC.visibility_of_element_located(MainPageLocators.PERSONAL_ACCOUNT_LINK)) + + +class TestPersonalAccount: + + def test_navigate_to_profile(self, driver): + wait = WebDriverWait(driver, 7) + + register_and_login(driver) + + current_url = driver.current_url + + wait.until(EC.element_to_be_clickable(MainPageLocators.PERSONAL_ACCOUNT_LINK)).click() + wait.until(EC.url_changes(current_url)) + + assert "/account" in driver.current_url + + def test_navigate_to_constructor_via_link(self, driver): + wait = WebDriverWait(driver, 7) + + register_and_login(driver) + + wait.until(EC.element_to_be_clickable(MainPageLocators.PERSONAL_ACCOUNT_LINK)).click() + wait.until(EC.visibility_of_element_located(ProfilePageLocators.LOGOUT_BUTTON)) + + current_url = driver.current_url + + wait.until(EC.element_to_be_clickable(MainPageLocators.CONSTRUCTOR_LINK)).click() + wait.until(EC.url_changes(current_url)) + + assert driver.current_url == BASE_URL + "/" + + def test_navigate_to_constructor_via_logo(self, driver): + wait = WebDriverWait(driver, 7) + + register_and_login(driver) + + wait.until(EC.element_to_be_clickable(MainPageLocators.PERSONAL_ACCOUNT_LINK)).click() + wait.until(EC.visibility_of_element_located(ProfilePageLocators.LOGOUT_BUTTON)) + + current_url = driver.current_url + + wait.until(EC.element_to_be_clickable(MainPageLocators.LOGO)).click() + wait.until(EC.url_changes(current_url)) + + assert driver.current_url == BASE_URL + "/" + + def test_logout(self, driver): + wait = WebDriverWait(driver, 7) + + register_and_login(driver) + + wait.until(EC.element_to_be_clickable(MainPageLocators.PERSONAL_ACCOUNT_LINK)).click() + wait.until(EC.visibility_of_element_located(ProfilePageLocators.LOGOUT_BUTTON)) + + current_url = driver.current_url + + wait.until(EC.element_to_be_clickable(ProfilePageLocators.LOGOUT_BUTTON)).click() + wait.until(EC.url_changes(current_url)) + + assert "/login" in driver.current_url \ No newline at end of file diff --git a/tests/test_registration.py b/tests/test_registration.py new file mode 100644 index 000000000..47dcbd89d --- /dev/null +++ b/tests/test_registration.py @@ -0,0 +1,40 @@ +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +from helpers import REGISTER_URL, LOGIN_URL, generate_email, generate_password +from locators.locators import RegisterPageLocators + + +class TestRegistration: + + def test_successful_registration(self, driver): + wait = WebDriverWait(driver, 5) + + driver.get(REGISTER_URL) + + email = generate_email() + password = generate_password() + + wait.until(EC.element_to_be_clickable(RegisterPageLocators.NAME_INPUT)).send_keys("Test User") + driver.find_element(*RegisterPageLocators.EMAIL_INPUT).send_keys(email) + driver.find_element(*RegisterPageLocators.PASSWORD_INPUT).send_keys(password) + driver.find_element(*RegisterPageLocators.REGISTER_BUTTON).click() + + wait.until(EC.url_to_be(LOGIN_URL)) + assert driver.current_url == LOGIN_URL + + def test_registration_with_invalid_password(self, driver): + wait = WebDriverWait(driver, 5) + + driver.get(REGISTER_URL) + + wait.until(EC.element_to_be_clickable(RegisterPageLocators.NAME_INPUT)).send_keys("Test User") + driver.find_element(*RegisterPageLocators.EMAIL_INPUT).send_keys(generate_email()) + driver.find_element(*RegisterPageLocators.PASSWORD_INPUT).send_keys("123") + driver.find_element(*RegisterPageLocators.REGISTER_BUTTON).click() + + error = wait.until( + EC.visibility_of_element_located(RegisterPageLocators.PASSWORD_ERROR) + ) + + assert error.is_displayed() \ No newline at end of file