diff --git a/pyproject.toml b/pyproject.toml index 57c520330c..384fe83e9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,3 @@ -[build-system] -build-backend = "hatchling.build" -requires = ["hatchling"] - [dependency-groups] dev = [ "ruff==0.11.8", @@ -9,7 +5,9 @@ dev = [ ] [project] -authors = [{name = "Nir Tal", email = "nirt236@gmail.com"}] +name = "selenium-python-example" +description = "Selenium Python example project with pytest and Allure report" +version = "0.1.0" dependencies = [ "allure-pytest==2.14.1", "assertpy==1.1", @@ -32,23 +30,10 @@ dependencies = [ "visual-regression-tracker==4.9.0", "xlrd==2.0.1" ] -description = "Selenium Python example project with pytest and Allure report" -name = "selenium-python-example" -readme = "README.md" requires-python = "~=3.11" -version = "0.1.0" - -[project.urls] -Homepage = "https://github.com/nirtal85/Selenium-Python-Example" -Repository = "https://github.com/nirtal85/Selenium-Python-Example" - -[tool.hatch.build.targets.sdist] -include = ["selenium_python_example"] - -[tool.hatch.build.targets.wheel] -include = ["selenium_python_example"] [tool.pytest.ini_options] +pythonpath = ["src"] addopts = [ "--clean-alluredir", "--alluredir=allure-results", diff --git a/enums/__init__.py b/src/enums/__init__.py similarity index 100% rename from enums/__init__.py rename to src/enums/__init__.py diff --git a/enums/status.py b/src/enums/status.py similarity index 100% rename from enums/status.py rename to src/enums/status.py diff --git a/pages/__init__.py b/src/pages/__init__.py similarity index 100% rename from pages/__init__.py rename to src/pages/__init__.py diff --git a/pages/about_page.py b/src/pages/about_page.py similarity index 93% rename from pages/about_page.py rename to src/pages/about_page.py index ae3656b008..8cd322af4b 100644 --- a/pages/about_page.py +++ b/src/pages/about_page.py @@ -1,7 +1,7 @@ import allure from selenium.webdriver.common.by import By -from pages.base_page import BasePage +from src.pages.base_page import BasePage class AboutPage(BasePage): diff --git a/pages/base_page.py b/src/pages/base_page.py similarity index 100% rename from pages/base_page.py rename to src/pages/base_page.py diff --git a/pages/forgot_password_page.py b/src/pages/forgot_password_page.py similarity index 96% rename from pages/forgot_password_page.py rename to src/pages/forgot_password_page.py index 1dfee6d8da..170bd10e81 100644 --- a/pages/forgot_password_page.py +++ b/src/pages/forgot_password_page.py @@ -1,7 +1,7 @@ import allure from selenium.webdriver.common.by import By -from pages.base_page import BasePage +from src.pages.base_page import BasePage class ForgotPasswordPage(BasePage): diff --git a/pages/login_page.py b/src/pages/login_page.py similarity index 96% rename from pages/login_page.py rename to src/pages/login_page.py index d74cd94c65..9661007a01 100644 --- a/pages/login_page.py +++ b/src/pages/login_page.py @@ -1,7 +1,7 @@ import allure from selenium.webdriver.common.by import By -from pages.top_bars.top_menu_bar import TopMenuBar +from src.pages.top_bars.top_menu_bar import TopMenuBar class LoginPage(TopMenuBar): diff --git a/pages/project_edit_page.py b/src/pages/project_edit_page.py similarity index 97% rename from pages/project_edit_page.py rename to src/pages/project_edit_page.py index b6d48104f6..e1b36fa9d2 100644 --- a/pages/project_edit_page.py +++ b/src/pages/project_edit_page.py @@ -1,7 +1,7 @@ import allure from selenium.webdriver.common.by import By -from pages.base_page import BasePage +from src.pages.base_page import BasePage class ProjectEditPage(BasePage): diff --git a/pages/project_type_page.py b/src/pages/project_type_page.py similarity index 93% rename from pages/project_type_page.py rename to src/pages/project_type_page.py index f73972fe34..4efe4492a6 100644 --- a/pages/project_type_page.py +++ b/src/pages/project_type_page.py @@ -2,7 +2,7 @@ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions -from pages.top_bars.top_navigate_bar import TopNavigateBar +from src.pages.top_bars.top_navigate_bar import TopNavigateBar class ProjectTypePage(TopNavigateBar): diff --git a/pages/projects_page.py b/src/pages/projects_page.py similarity index 98% rename from pages/projects_page.py rename to src/pages/projects_page.py index 5e17d9c582..7d734e6943 100644 --- a/pages/projects_page.py +++ b/src/pages/projects_page.py @@ -2,8 +2,8 @@ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions -from enums.status import Status -from pages.top_bars.top_navigate_bar import TopNavigateBar +from src.enums.status import Status +from src.pages.top_bars.top_navigate_bar import TopNavigateBar class ProjectsPage(TopNavigateBar): diff --git a/pages/templates_page.py b/src/pages/templates_page.py similarity index 94% rename from pages/templates_page.py rename to src/pages/templates_page.py index 7b871de670..d0d9e347b2 100644 --- a/pages/templates_page.py +++ b/src/pages/templates_page.py @@ -1,7 +1,7 @@ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions -from pages.top_bars.top_navigate_bar import TopNavigateBar +from src.pages.top_bars.top_navigate_bar import TopNavigateBar class TemplatesPage(TopNavigateBar): diff --git a/pages/top_bars/__init__.py b/src/pages/top_bars/__init__.py similarity index 100% rename from pages/top_bars/__init__.py rename to src/pages/top_bars/__init__.py diff --git a/pages/top_bars/top_menu_bar.py b/src/pages/top_bars/top_menu_bar.py similarity index 93% rename from pages/top_bars/top_menu_bar.py rename to src/pages/top_bars/top_menu_bar.py index a591f46157..d1c5bd8447 100644 --- a/pages/top_bars/top_menu_bar.py +++ b/src/pages/top_bars/top_menu_bar.py @@ -1,7 +1,7 @@ import allure from selenium.webdriver.common.by import By -from pages.base_page import BasePage +from src.pages.base_page import BasePage class TopMenuBar(BasePage): diff --git a/pages/top_bars/top_navigate_bar.py b/src/pages/top_bars/top_navigate_bar.py similarity index 97% rename from pages/top_bars/top_navigate_bar.py rename to src/pages/top_bars/top_navigate_bar.py index b5b329424e..20da4d3a2a 100644 --- a/pages/top_bars/top_navigate_bar.py +++ b/src/pages/top_bars/top_navigate_bar.py @@ -1,7 +1,7 @@ import allure from selenium.webdriver.common.by import By -from pages.base_page import BasePage +from src.pages.base_page import BasePage class TopNavigateBar(BasePage): diff --git a/utilities/__init__.py b/src/utilities/__init__.py similarity index 100% rename from utilities/__init__.py rename to src/utilities/__init__.py diff --git a/utilities/constants.py b/src/utilities/constants.py similarity index 100% rename from utilities/constants.py rename to src/utilities/constants.py diff --git a/utilities/data.py b/src/utilities/data.py similarity index 100% rename from utilities/data.py rename to src/utilities/data.py diff --git a/utilities/excel_parser.py b/src/utilities/excel_parser.py similarity index 95% rename from utilities/excel_parser.py rename to src/utilities/excel_parser.py index d65c2704cf..f32a709447 100644 --- a/utilities/excel_parser.py +++ b/src/utilities/excel_parser.py @@ -2,7 +2,7 @@ import xlrd -from utilities.constants import Constants +from src.utilities.constants import Constants class ExcelParser: diff --git a/utilities/mailinator_helper.py b/src/utilities/mailinator_helper.py similarity index 100% rename from utilities/mailinator_helper.py rename to src/utilities/mailinator_helper.py diff --git a/utilities/vrt_helper.py b/src/utilities/vrt_helper.py similarity index 99% rename from utilities/vrt_helper.py rename to src/utilities/vrt_helper.py index 854f255596..898bbe958c 100644 --- a/utilities/vrt_helper.py +++ b/src/utilities/vrt_helper.py @@ -10,7 +10,7 @@ from selenium.webdriver.support.wait import WebDriverWait from visual_regression_tracker import IgnoreArea, TestRun, TestRunStatus, VisualRegressionTracker -from utilities.constants import Constants +from src.utilities.constants import Constants class VrtHelper: diff --git a/utilities/web_driver_listener.py b/src/utilities/web_driver_listener.py similarity index 100% rename from utilities/web_driver_listener.py rename to src/utilities/web_driver_listener.py diff --git a/tests/base_test.py b/tests/base_test.py index e694f13bec..f315ab8b3e 100644 --- a/tests/base_test.py +++ b/tests/base_test.py @@ -3,15 +3,15 @@ from selenium.webdriver import Chrome, Edge, Firefox from selenium.webdriver.support.wait import WebDriverWait -from pages.about_page import AboutPage -from pages.forgot_password_page import ForgotPasswordPage -from pages.login_page import LoginPage -from pages.project_edit_page import ProjectEditPage -from pages.project_type_page import ProjectTypePage -from pages.projects_page import ProjectsPage -from pages.templates_page import TemplatesPage -from utilities.mailinator_helper import MailinatorHelper -from utilities.vrt_helper import VrtHelper +from src.pages.about_page import AboutPage +from src.pages.forgot_password_page import ForgotPasswordPage +from src.pages.login_page import LoginPage +from src.pages.project_edit_page import ProjectEditPage +from src.pages.project_type_page import ProjectTypePage +from src.pages.projects_page import ProjectsPage +from src.pages.templates_page import TemplatesPage +from src.utilities.mailinator_helper import MailinatorHelper +from src.utilities.vrt_helper import VrtHelper class BaseTest(ABC): diff --git a/tests/conftest.py b/tests/conftest.py index 513c04f525..dddb42b94f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -20,19 +20,19 @@ from selenium.webdriver.support.wait import WebDriverWait from visual_regression_tracker import VisualRegressionTracker -from pages.about_page import AboutPage -from pages.forgot_password_page import ForgotPasswordPage -from pages.login_page import LoginPage -from pages.project_edit_page import ProjectEditPage -from pages.project_type_page import ProjectTypePage -from pages.projects_page import ProjectsPage -from pages.templates_page import TemplatesPage -from utilities.constants import Constants -from utilities.data import Data -from utilities.excel_parser import ExcelParser -from utilities.mailinator_helper import MailinatorHelper -from utilities.vrt_helper import VrtHelper -from utilities.web_driver_listener import DriverEventListener +from src.pages.about_page import AboutPage +from src.pages.forgot_password_page import ForgotPasswordPage +from src.pages.login_page import LoginPage +from src.pages.project_edit_page import ProjectEditPage +from src.pages.project_type_page import ProjectTypePage +from src.pages.projects_page import ProjectsPage +from src.pages.templates_page import TemplatesPage +from src.utilities.constants import Constants +from src.utilities.mailinator_helper import MailinatorHelper +from src.utilities.web_driver_listener import DriverEventListener +from src.utilities.excel_parser import ExcelParser +from src.utilities.data import Data +from src.utilities.vrt_helper import VrtHelper drivers = ("chrome", "firefox", "chrome_headless", "remote") diff --git a/tests/db_test.py b/tests/db_test.py index e8fc96fc56..f3e8c2ffe2 100644 --- a/tests/db_test.py +++ b/tests/db_test.py @@ -6,7 +6,7 @@ @pytest.mark.skip(reason="requires database connection") class TestDatabaseExample: @allure.title("Verify population amounts") - def test_verify_population_amount(self, db_connection): + def test_verify_population_amount(self, db_connection) -> None: with db_connection.cursor() as cursor: cursor.execute("SELECT Population FROM city WHERE CountryCode='DNK'") population_amount = [item[0] for item in cursor.fetchall()] diff --git a/tests/dependency_class_test.py b/tests/dependency_class_test.py index b7756ecb25..a3cd60845c 100644 --- a/tests/dependency_class_test.py +++ b/tests/dependency_class_test.py @@ -10,7 +10,7 @@ class TestDependencyExample(BaseTest): classes. """ - def test_e(self): + def test_e(self) -> None: """Placeholder test function with the dependency name "e" and depends on "TestDependencyExample::b". This test case is designed for demonstration purposes only. diff --git a/tests/dependency_test.py b/tests/dependency_test.py index dfa4ea28a8..5cc975318d 100644 --- a/tests/dependency_test.py +++ b/tests/dependency_test.py @@ -18,17 +18,17 @@ class TestDependencyExample(BaseTest): """ @pytest.mark.dependency(name="a") - def test_a(self): + def test_a(self) -> None: pass @pytest.mark.dependency(name="b") - def test_b(self): + def test_b(self) -> None: assert False @pytest.mark.dependency(name="c", depends=["b"]) - def test_c(self): + def test_c(self) -> None: pass @pytest.mark.dependency(name="d", depends=["b", "c"]) - def test_d(self): + def test_d(self) -> None: pass diff --git a/tests/email_test.py b/tests/email_test.py index a03f87dd50..791fe4b4b5 100644 --- a/tests/email_test.py +++ b/tests/email_test.py @@ -9,17 +9,17 @@ @pytest.mark.skip(reason="requires mailinator client") class TestEmail(BaseTest): @allure.title("Verify email count in user inbox") - def test_verify_email_count(self, mailinator_helper): + def test_verify_email_count(self, mailinator_helper) -> None: subject_counts = mailinator_helper.count_messages_by_subject("testautomation") TestCase().assertDictEqual({"some subject": 1}, subject_counts) @allure.title("Verify email content") - def test_verify_email_body(self, mailinator_helper): + def test_verify_email_body(self, mailinator_helper) -> None: message = mailinator_helper.get_message("testautomation", "purchase is confirmed") assert "Thank you for your purchase" in message.parts[0].body @allure.title("Get OTP code from email") - def test_verify_otp_code(self, mailinator_helper): + def test_verify_otp_code(self, mailinator_helper) -> None: otp_code = mailinator_helper.get_otp_code("testautomation") assert otp_code.isdigit(), f"OTP code '{otp_code}' is not a digit string" assert len(otp_code) == 6, f"OTP code '{otp_code}' is not 6 digits long" diff --git a/tests/forgot_password_test.py b/tests/forgot_password_test.py index f6df883e38..9ed5e785b1 100644 --- a/tests/forgot_password_test.py +++ b/tests/forgot_password_test.py @@ -4,8 +4,8 @@ import pytest from assertpy import assert_that +from src.utilities.data import Data from tests.base_test import BaseTest -from utilities.data import Data @allure.epic("Security") @@ -18,7 +18,7 @@ class TestForgotPassword(BaseTest): @allure.description("Forgot password with a valid email address") @allure.title("Forgot Password with valid email test") - def test_valid_email(self, data: Data): + def test_valid_email(self, data: Data) -> None: self.about_page.click_login_link() self.login_page.click_forgot_password() self.forget_password_page.send_password_reset_link(os.getenv("EMAIL")) @@ -32,7 +32,7 @@ def test_valid_email(self, data: Data): "'involve' in config.getoption('base_url')", reason="Conditional skip based on base url", ) - def test_invalid_email(self, excel_reader, data: Data): + def test_invalid_email(self, excel_reader, data: Data) -> None: """This test is an example of a conditional skip based on base url.""" emails = excel_reader.read_from_excel("Emails") self.about_page.click_login_link() @@ -47,7 +47,7 @@ def test_invalid_email(self, excel_reader, data: Data): @allure.link("github.com/allure-examples/", name="Allure Examples") @allure.issue("github.com/allure-examples/allure-examples/issues/1", name="ISSUE-1") @allure.testcase("github.com/allure-examples/allure-examples/issues/2", name="TESTCASE-2") - def test_expected_exception_on_page_title(self): + def test_expected_exception_on_page_title(self) -> None: self.about_page.click_login_link() self.login_page.click_forgot_password() with pytest.raises(AssertionError) as e: diff --git a/tests/login_test.py b/tests/login_test.py index 6ca5b514c9..cd2ecfa466 100644 --- a/tests/login_test.py +++ b/tests/login_test.py @@ -7,8 +7,8 @@ from assertpy import assert_that from tests.base_test import BaseTest -from utilities.constants import Constants -from utilities.data import Data +from src.utilities.constants import Constants +from src.utilities.data import Data users = [("nirt236@gmail.com", "123456"), ("elias@gmail.com", "12345Tr")] @@ -22,7 +22,7 @@ class TestLogin(BaseTest): @allure.title("Login with invalid credentials test") @pytest.mark.parametrize("email, password", users) @pytest.mark.run(order=3) - def test_invalid_login(self, email: str, password: str, data: Data): + def test_invalid_login(self, email: str, password: str, data: Data) -> None: self.about_page.click_login_link() self.login_page.login(email, password) assert_that(self.login_page.get_error_message()).described_as( @@ -31,14 +31,14 @@ def test_invalid_login(self, email: str, password: str, data: Data): @allure.description("Basic sanity") @pytest.mark.devRun - def test_sanity(self, base_url): + def test_sanity(self, base_url) -> None: assert_that(self.driver.current_url).described_as("URL").is_equal_to(base_url) @allure.description("valid login") @allure.title("Login with valid credentials test") @allure.tag("Tagged test") @pytest.mark.flaky(reruns=1) - def test_valid_login(self, data: Data): + def test_valid_login(self, data: Data) -> None: self.about_page.set_geo_location(30.3079823, -97.893803) self.about_page.click_login_link() self.login_page.login(os.getenv("EMAIL"), os.getenv("PASSWORD")) @@ -49,7 +49,7 @@ def test_valid_login(self, data: Data): @allure.description("Log out from app") @allure.title("Logout of system test") @allure.story("As a user I want to be able to logout after a successful login.") - def test_logout(self, data: Data): + def test_logout(self, data: Data) -> None: """Test case to verify the logout functionality. :param data: An instance of the Data dataclass containing test data. @@ -136,5 +136,5 @@ def test_logout(self, data: Data): @allure.title("Skipped test example") @allure.label("owner", "nir tal") @pytest.mark.skip(reason="skip test example") - def test_skip(self): + def test_skip(self) -> None: pass diff --git a/tests/visual_test.py b/tests/visual_test.py index be03f937b4..b60b209c12 100644 --- a/tests/visual_test.py +++ b/tests/visual_test.py @@ -13,11 +13,11 @@ @pytest.mark.skip(reason="requires a running VRT server") class TestVisual(BaseTest): @allure.title("Visual test of login page") - def test_shoot_page(self, vrt_helper): + def test_shoot_page(self, vrt_helper) -> None: vrt_helper.shoot_page("page baseline") @allure.title("Visual test of login page with ignored area") - def test_shoot_page_with_ignore_area(self, vrt_helper): + def test_shoot_page_with_ignore_area(self, vrt_helper) -> None: element_to_ignore: WebElement = self.wait.until( expected_conditions.visibility_of_element_located((By.CSS_SELECTOR, "h1")) ) @@ -27,5 +27,5 @@ def test_shoot_page_with_ignore_area(self, vrt_helper): ) @allure.title("Visual test of login page element") - def test_shoot_element(self, vrt_helper): + def test_shoot_element(self, vrt_helper) -> None: vrt_helper.shoot_element("element baseline", (By.CSS_SELECTOR, "h1")) diff --git a/tests/workspaces_test.py b/tests/workspaces_test.py index 10ba33b39c..42bbe31980 100644 --- a/tests/workspaces_test.py +++ b/tests/workspaces_test.py @@ -4,11 +4,11 @@ import pytest from assertpy import assert_that -from enums.status import Status -from pages.about_page import AboutPage -from pages.login_page import LoginPage +from src.enums.status import Status +from src.pages.about_page import AboutPage +from src.pages.login_page import LoginPage +from src.utilities.data import Data from tests.base_test import BaseTest -from utilities.data import Data def login(about_page: AboutPage, login_page: LoginPage): @@ -27,7 +27,7 @@ def setup_method_fixture(self): @allure.description("Create new Workspace") @allure.title("Create new workspace test") @pytest.mark.run(order=1) - def test_create_new_workspace(self, data: Data): + def test_create_new_workspace(self, data: Data) -> None: before = self.projects_page.get_workspaces_number() self.projects_page.create_workspace(data.workspace.name) after = self.projects_page.get_workspaces_number() @@ -36,7 +36,7 @@ def test_create_new_workspace(self, data: Data): @allure.description("Rename an existing workspace") @allure.title("Rename an existing workspace test") @pytest.mark.run(order=2) - def test_rename_workspace(self, data: Data): + def test_rename_workspace(self, data: Data) -> None: self.projects_page.rename_workspace(data.workspace.name, data.workspace.new_name) assert_that(self.projects_page.is_workspace_found(data.workspace.new_name)).described_as( "status" @@ -45,7 +45,7 @@ def test_rename_workspace(self, data: Data): @allure.description("Delete an existing workspace") @allure.title("Delete existing workspace") @pytest.mark.run(order=3) - def test_delete_workspace(self): + def test_delete_workspace(self) -> None: before = self.projects_page.get_workspaces_number() self.projects_page.delete_workspace() after = self.projects_page.get_workspaces_number() @@ -56,7 +56,7 @@ def test_delete_workspace(self): ) @allure.title("Number of projects displayed in page test") @pytest.mark.run(order=4) - def test_number_of_existing_projects(self): + def test_number_of_existing_projects(self) -> None: number_of_displayed_projects = self.projects_page.get_projects_number_in_page() number_of_projects_in_workspace = self.projects_page.get_projects_number_from_workspace() assert_that(number_of_displayed_projects).described_as( @@ -66,7 +66,7 @@ def test_number_of_existing_projects(self): @allure.description("Selecting and adding a project to workspace") @allure.title("Add project to workspace test") @pytest.mark.run(order=5) - def test_add_project_to_workspace(self, data: Data): + def test_add_project_to_workspace(self, data: Data) -> None: before = self.projects_page.get_projects_number_in_page() self.projects_page.create_new_project() self.project_type_page.select_project(data.workspace.project_type) @@ -82,7 +82,7 @@ def test_add_project_to_workspace(self, data: Data): @allure.description("Search for an existing project") @allure.title("Search for existing project test") @pytest.mark.run(order=6) - def test_search_project(self, data: Data): + def test_search_project(self, data: Data) -> None: self.projects_page.search_project(data.workspace.project_name) expected_status = self.projects_page.is_project_found(data.workspace.project_name) assert_that(expected_status).described_as("status").is_true() @@ -90,7 +90,7 @@ def test_search_project(self, data: Data): @allure.description("Search for a non existing project") @allure.title("Search for non existing project") @pytest.mark.run(order=7) - def test_search_for_non_existing_project(self, data: Data): + def test_search_for_non_existing_project(self, data: Data) -> None: self.projects_page.search_project(data.workspace.non_existing_project) assert_that(self.projects_page.get_no_project_found_message()).described_as( "not found message" @@ -99,7 +99,7 @@ def test_search_for_non_existing_project(self, data: Data): @allure.description("Cancel project deletion") @allure.title("Cancel a project deletion") @pytest.mark.run(order=8) - def test_cancel_project_deletion(self, data: Data): + def test_cancel_project_deletion(self, data: Data) -> None: before = self.projects_page.get_projects_number_in_page() self.projects_page.delete_project(data.workspace.project_name, Status.CANCEL.value) after = self.projects_page.get_projects_number_in_page() @@ -108,12 +108,11 @@ def test_cancel_project_deletion(self, data: Data): @allure.description("Deleting an existing project from workspace") @allure.title("Delete existing project") @pytest.mark.run(order=9) - def test_delete_project(self, data: Data): - with allure.step("grand parent step"): - with allure.step("parent step"): - before = self.projects_page.get_projects_number_in_page() - self.projects_page.delete_project(data.workspace.project_name) - after = self.projects_page.get_projects_number_in_page() - assert_that(before).described_as("number of displayed projects").is_equal_to( - after + 1 - ) + def test_delete_project(self, data: Data) -> None: + with allure.step("grand parent step"), allure.step("parent step"): + before = self.projects_page.get_projects_number_in_page() + self.projects_page.delete_project(data.workspace.project_name) + after = self.projects_page.get_projects_number_in_page() + assert_that(before).described_as("number of displayed projects").is_equal_to( + after + 1 + )