finsh commit

This commit is contained in:
2025-09-14 08:51:47 +08:00
commit 838806b9e3
60 changed files with 5697 additions and 0 deletions

0
page_objects/__init__.py Normal file
View File

179
page_objects/base_page.py Normal file
View 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))

View 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

View 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)

View File

View File