finsh commit
This commit is contained in:
0
page_objects/__init__.py
Normal file
0
page_objects/__init__.py
Normal file
179
page_objects/base_page.py
Normal file
179
page_objects/base_page.py
Normal file
@@ -0,0 +1,179 @@
|
||||
from selenium.webdriver.remote.webdriver import WebDriver
|
||||
from selenium.webdriver.remote.webelement import WebElement
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
import time
|
||||
|
||||
class BasePage:
|
||||
"""
|
||||
The BasePage class serves as a foundation for all page objects.
|
||||
It encapsulates common Selenium operations to promote code reuse and
|
||||
improve test maintenance.
|
||||
"""
|
||||
|
||||
def __init__(self, driver: WebDriver, base_url: str = "http://120.53.89.168:90"):
|
||||
"""
|
||||
Initializes the BasePage with a WebDriver instance and a base URL.
|
||||
|
||||
:param driver: The WebDriver instance to interact with the browser.
|
||||
:param base_url: The base URL of the web application under test.
|
||||
"""
|
||||
self.driver = driver
|
||||
self.base_url = base_url
|
||||
self.wait = WebDriverWait(driver, 10) # Default explicit wait of 10 seconds
|
||||
|
||||
def open_url(self, path: str):
|
||||
"""
|
||||
Navigates to a specific path relative to the base URL.
|
||||
|
||||
:param path: The relative path to open (e.g., "/form-elements").
|
||||
"""
|
||||
url = self.base_url + path
|
||||
self.driver.get(url)
|
||||
|
||||
def find_element(self, locator: tuple) -> WebElement:
|
||||
"""
|
||||
Finds and returns a web element using an explicit wait.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value
|
||||
(e.g., (By.ID, "my-element")).
|
||||
:return: The located WebElement.
|
||||
"""
|
||||
return self.wait.until(EC.presence_of_element_located(locator))
|
||||
|
||||
def find_visible_element(self, locator: tuple) -> WebElement:
|
||||
"""
|
||||
Finds and returns a web element that is visible on the page.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value.
|
||||
:return: The located and visible WebElement.
|
||||
"""
|
||||
return self.wait.until(EC.visibility_of_element_located(locator))
|
||||
|
||||
def click(self, locator: tuple):
|
||||
"""
|
||||
Waits for an element to be clickable and then clicks on it.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value.
|
||||
"""
|
||||
element = self.wait.until(EC.element_to_be_clickable(locator))
|
||||
element.click()
|
||||
|
||||
def send_keys(self, locator: tuple, text: str):
|
||||
"""
|
||||
Finds an element, clears its content, and sends keys to it.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value.
|
||||
:param text: The text to send to the element.
|
||||
"""
|
||||
element = self.find_element(locator)
|
||||
element.clear()
|
||||
element.send_keys(text)
|
||||
|
||||
def get_text(self, locator: tuple) -> str:
|
||||
"""
|
||||
Finds an element and returns its text content.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value.
|
||||
:return: The text content of the element.
|
||||
"""
|
||||
element = self.find_element(locator)
|
||||
return element.text
|
||||
|
||||
def is_element_selected(self, locator: tuple) -> bool:
|
||||
"""
|
||||
Checks if a checkbox or radio button element is selected.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value.
|
||||
:return: True if the element is selected, False otherwise.
|
||||
"""
|
||||
element = self.find_element(locator)
|
||||
return element.is_selected()
|
||||
|
||||
def is_element_enabled(self, locator: tuple) -> bool:
|
||||
"""
|
||||
Checks if a form element is enabled.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value.
|
||||
:return: True if the element is enabled, False otherwise.
|
||||
"""
|
||||
element = self.find_element(locator)
|
||||
return element.is_enabled()
|
||||
|
||||
def switch_to_alert(self):
|
||||
"""
|
||||
Switches the driver's focus to a browser alert.
|
||||
|
||||
:return: The alert object.
|
||||
"""
|
||||
return self.wait.until(EC.alert_is_present())
|
||||
|
||||
|
||||
def back(self):
|
||||
"""
|
||||
Navigates one step backward in the browser history.
|
||||
"""
|
||||
self.driver.back()
|
||||
|
||||
def forward(self):
|
||||
"""
|
||||
Navigates one step forward in the browser history.
|
||||
"""
|
||||
self.driver.forward()
|
||||
|
||||
def refresh(self):
|
||||
"""
|
||||
Refreshes the current page.
|
||||
"""
|
||||
self.driver.refresh()
|
||||
|
||||
def take_screenshot(self, name: str = "screenshot"):
|
||||
"""
|
||||
Captures a screenshot of the current browser window.
|
||||
|
||||
:param name: Base name for the screenshot file.
|
||||
:return: The filename of the saved screenshot.
|
||||
"""
|
||||
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"{name}_{timestamp}.png"
|
||||
self.driver.save_screenshot(filename)
|
||||
return filename
|
||||
|
||||
def find_elements(self, locator: tuple):
|
||||
"""
|
||||
Finds and returns a list of web elements using an explicit wait.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value
|
||||
(e.g., (By.CLASS_NAME, "items")).
|
||||
:return: A list of located WebElements.
|
||||
"""
|
||||
return self.wait.until(EC.presence_of_all_elements_located(locator))
|
||||
|
||||
def scroll_into_view(self, locator: tuple):
|
||||
"""
|
||||
Scrolls the page until the specified element is in view.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value.
|
||||
"""
|
||||
element = self.find_element(locator)
|
||||
self.driver.execute_script("arguments[0].scrollIntoView(true);", element)
|
||||
|
||||
def click_by_js(self, locator: tuple):
|
||||
"""
|
||||
Clicks on an element using JavaScript execution.
|
||||
Useful when standard Selenium click does not work.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value.
|
||||
"""
|
||||
element = self.find_element(locator)
|
||||
self.driver.execute_script("arguments[0].click();", element)
|
||||
|
||||
def wait_for_text(self, locator: tuple, text: str):
|
||||
"""
|
||||
Waits until the specified text is present within an element.
|
||||
|
||||
:param locator: A tuple containing the locator strategy and value.
|
||||
:param text: The text to wait for in the element.
|
||||
:return: True if the text is present, False otherwise.
|
||||
"""
|
||||
return self.wait.until(EC.text_to_be_present_in_element(locator, text))
|
||||
107
page_objects/dynamic_content_page.py
Normal file
107
page_objects/dynamic_content_page.py
Normal file
@@ -0,0 +1,107 @@
|
||||
import time
|
||||
from selenium.webdriver.common.by import By
|
||||
from .base_page import BasePage
|
||||
|
||||
class DynamicContentPage(BasePage):
|
||||
"""
|
||||
Page Object for the Dynamic Content test page.
|
||||
"""
|
||||
|
||||
# --- Locators ---
|
||||
_DELAYED_TEXT = (By.ID, "delayed-text")
|
||||
_ENABLE_BUTTON = (By.XPATH, "//button[text()='Enable Button']")
|
||||
_INITIALLY_DISABLED_BUTTON = (By.XPATH, "//button[text()='Initially Disabled']")
|
||||
By.XPATH, '//*[@id="radix-«r0»-trigger-password"]'
|
||||
_ACCOUNT_TAB = (By.XPATH, "//button[@role='tab' and contains(@id,'trigger-account')]")
|
||||
_PASSWORD_TAB = (By.XPATH, "//button[@role='tab' and contains(@id,'trigger-password')]")
|
||||
_ACTIVE_TAB_CONTENT = (By.XPATH, "//div[@role='tabpanel' and @data-state='active']")
|
||||
_SHOW_ALERT_BUTTON = (By.XPATH, "//button[text()='Show Alert']")
|
||||
_OPEN_MODAL_BUTTON = (By.XPATH, "//button[text()='Open Modal']")
|
||||
_MODAL_TITLE = (By.ID, "radix-") # This ID is dynamic, a better locator is needed.
|
||||
# A better locator for the modal title would be:
|
||||
_MODAL_TITLE_BETTER = (By.XPATH, "//h2[text()='Modal Title']")
|
||||
|
||||
|
||||
def __init__(self, driver):
|
||||
"""Initializes the DynamicContentPage with the WebDriver."""
|
||||
super().__init__(driver)
|
||||
self.page_path = "/dynamic-content"
|
||||
|
||||
# --- Page Actions ---
|
||||
|
||||
def open(self):
|
||||
"""Navigates to the dynamic content page."""
|
||||
self.open_url(self.page_path)
|
||||
|
||||
def get_delayed_text(self) -> str:
|
||||
"""
|
||||
Waits for the delayed text to be visible and returns its content.
|
||||
The wait is handled by find_visible_element.
|
||||
"""
|
||||
# We need to wait for the text "Loading..." to disappear first.
|
||||
# This is a more complex wait condition. For simplicity, we'll just wait for visibility.
|
||||
time.sleep(4)
|
||||
element = self.find_visible_element(self._DELAYED_TEXT)
|
||||
return element.text
|
||||
|
||||
def click_enable_button(self):
|
||||
"""Clicks the button that enables the initially disabled button."""
|
||||
self.click(self._ENABLE_BUTTON)
|
||||
|
||||
def is_initially_disabled_button_enabled(self) -> bool:
|
||||
"""Checks if the initially disabled button is now enabled."""
|
||||
return self.is_element_enabled(self._INITIALLY_DISABLED_BUTTON)
|
||||
|
||||
def switch_to_tab(self, tab_name: str):
|
||||
"""
|
||||
Switches to the specified tab.
|
||||
:param tab_name: 'account' or 'password'.
|
||||
"""
|
||||
if tab_name.lower() == 'account':
|
||||
self.click(self._ACCOUNT_TAB)
|
||||
elif tab_name.lower() == 'password':
|
||||
self.click(self._PASSWORD_TAB)
|
||||
else:
|
||||
raise ValueError("Invalid tab name. Must be 'account' or 'password'.")
|
||||
time.sleep(1) # Add a short delay to allow the tab content to update
|
||||
|
||||
def get_active_tab_content(self) -> str:
|
||||
"""
|
||||
Gets the text of the currently visible tab panel.
|
||||
This requires checking which panel is visible.
|
||||
"""
|
||||
element = self.find_visible_element(self._ACTIVE_TAB_CONTENT)
|
||||
return element.text
|
||||
|
||||
|
||||
# def get_active_tab_content(self) -> str:
|
||||
# """
|
||||
# Gets the text of the currently active tab panel based on hidden attribute.
|
||||
# """
|
||||
# panels = [self._ACCOUNT_TAB_CONTENT, self._PASSWORD_TAB_CONTENT]
|
||||
# for panel in panels:
|
||||
# element = self.find_element(panel)
|
||||
# if element.get_attribute("hidden") in [None, "false"]:
|
||||
# return element.text
|
||||
# return ""
|
||||
|
||||
def trigger_alert(self):
|
||||
"""Clicks the button to show a browser alert."""
|
||||
self.click(self._SHOW_ALERT_BUTTON)
|
||||
|
||||
def get_alert_text_and_accept(self) -> str:
|
||||
"""Switches to an alert, gets its text, and accepts it."""
|
||||
alert = self.switch_to_alert()
|
||||
text = alert.text
|
||||
alert.accept()
|
||||
return text
|
||||
|
||||
def open_modal(self):
|
||||
"""Clicks the button to open the modal dialog."""
|
||||
self.click(self._OPEN_MODAL_BUTTON)
|
||||
|
||||
def get_modal_title(self) -> str:
|
||||
"""Waits for the modal to be visible and returns its title."""
|
||||
# Using the more robust locator
|
||||
title_element = self.find_visible_element(self._MODAL_TITLE_BETTER)
|
||||
return title_element.text
|
||||
127
page_objects/form_elements_page.py
Normal file
127
page_objects/form_elements_page.py
Normal file
@@ -0,0 +1,127 @@
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import Select
|
||||
from .base_page import BasePage
|
||||
|
||||
class FormElementsPage(BasePage):
|
||||
"""
|
||||
Page Object for the Form Elements test page.
|
||||
Encapsulates all locators and actions related to this page.
|
||||
"""
|
||||
|
||||
# --- Locators ---
|
||||
_TEXT_INPUT = (By.ID, "text-input")
|
||||
_CHECKBOX = (By.ID, "checkbox-input")
|
||||
_RADIO_OPTION_ONE = (By.ID, "r1")
|
||||
_RADIO_OPTION_TWO = (By.ID, "r2")
|
||||
_RADIO_OPTION_THREE = (By.ID, "r3")
|
||||
_SELECT_DROPDOWN = (By.ID, "select-input")
|
||||
_TEXTAREA = (By.ID, "textarea-input")
|
||||
_SWITCH = (By.ID, "switch-input")
|
||||
_SUBMIT_BUTTON = (By.XPATH, "//button[@type='submit']")
|
||||
_CANCEL_BUTTON = (By.XPATH, "//button[text()='Cancel']")
|
||||
_DISABLED_BUTTON = (By.XPATH, "//button[text()='Disabled']")
|
||||
|
||||
def __init__(self, driver):
|
||||
"""Initializes the FormElementsPage with the WebDriver."""
|
||||
super().__init__(driver)
|
||||
self.page_path = "/form-elements"
|
||||
|
||||
# --- Page Actions ---
|
||||
|
||||
def open(self):
|
||||
"""Navigates to the form elements page."""
|
||||
self.open_url(self.page_path)
|
||||
|
||||
def enter_text_in_input(self, text: str):
|
||||
"""Enters text into the main text input field."""
|
||||
self.send_keys(self._TEXT_INPUT, text)
|
||||
|
||||
def get_text_from_input(self) -> str:
|
||||
"""Gets the current value from the text input field."""
|
||||
return self.find_element(self._TEXT_INPUT).get_attribute("value")
|
||||
|
||||
def select_checkbox(self):
|
||||
"""Clicks the checkbox to select it."""
|
||||
self.click(self._CHECKBOX)
|
||||
|
||||
def is_checkbox_selected(self) -> bool:
|
||||
"""Checks if the checkbox is selected."""
|
||||
checkbox = self.find_element(self._CHECKBOX)
|
||||
return checkbox.get_attribute("data-state") == "checked"
|
||||
|
||||
def choose_radio_option(self, option: int):
|
||||
"""
|
||||
Selects a radio button option.
|
||||
:param option: 1 for Option One, 2 for Option Two, 3 for Option Three.
|
||||
"""
|
||||
if option == 1:
|
||||
self.click(self._RADIO_OPTION_ONE)
|
||||
elif option == 2:
|
||||
self.click(self._RADIO_OPTION_TWO)
|
||||
elif option == 3:
|
||||
self.click(self._RADIO_OPTION_THREE)
|
||||
else:
|
||||
raise ValueError("Invalid option number. Must be 1, 2, or 3.")
|
||||
|
||||
def is_radio_option_selected(self, option: int) -> bool:
|
||||
"""Checks if a specific radio button option is selected."""
|
||||
locator = None
|
||||
if option == 1:
|
||||
locator = self._RADIO_OPTION_ONE
|
||||
elif option == 2:
|
||||
locator = self._RADIO_OPTION_TWO
|
||||
elif option == 3:
|
||||
locator = self._RADIO_OPTION_THREE
|
||||
else:
|
||||
return False
|
||||
|
||||
radio_button = self.find_element(locator)
|
||||
return radio_button.get_attribute("data-state") == "checked"
|
||||
|
||||
def select_fruit_by_visible_text(self, text: str):
|
||||
"""
|
||||
Selects an option from the custom shadcn dropdown by its visible text.
|
||||
:param text: The visible text of the option to select (e.g., "Apple").
|
||||
"""
|
||||
# 1. Click the dropdown trigger to open the options
|
||||
self.click(self._SELECT_DROPDOWN)
|
||||
|
||||
# 2. Define the locator for the desired option based on its text
|
||||
# The options are typically in a popover, so we wait for them to be visible.
|
||||
option_locator = (By.XPATH, f"//div[@role='option' and .//span[text()='{text}']]")
|
||||
|
||||
# 3. Click the option
|
||||
self.click(option_locator)
|
||||
|
||||
def get_selected_fruit(self) -> str:
|
||||
"""Gets the currently selected value from the dropdown trigger."""
|
||||
return self.get_text(self._SELECT_DROPDOWN)
|
||||
|
||||
def enter_message_in_textarea(self, message: str):
|
||||
"""Enters text into the textarea field."""
|
||||
self.send_keys(self._TEXTAREA, message)
|
||||
|
||||
def get_message_from_textarea(self) -> str:
|
||||
"""Gets the current value from the textarea field."""
|
||||
return self.find_element(self._TEXTAREA).get_attribute("value")
|
||||
|
||||
def toggle_switch(self):
|
||||
"""Clicks the switch to toggle its state."""
|
||||
self.click(self._SWITCH)
|
||||
|
||||
def is_switch_on(self) -> bool:
|
||||
"""
|
||||
Checks if the switch is in the 'on' state.
|
||||
This depends on how state is represented (e.g., aria-checked, class).
|
||||
For shadcn, it's often a data attribute `data-state`.
|
||||
"""
|
||||
switch_element = self.find_element(self._SWITCH)
|
||||
return switch_element.get_attribute("data-state") == "checked"
|
||||
|
||||
def click_submit_button(self):
|
||||
"""Clicks the submit button."""
|
||||
self.click(self._SUBMIT_BUTTON)
|
||||
|
||||
def is_disabled_button_enabled(self) -> bool:
|
||||
"""Checks if the 'Disabled' button is enabled."""
|
||||
return self.is_element_enabled(self._DISABLED_BUTTON)
|
||||
0
page_objects/home_page.py
Normal file
0
page_objects/home_page.py
Normal file
0
page_objects/login_page.py
Normal file
0
page_objects/login_page.py
Normal file
Reference in New Issue
Block a user