Coverage for src / ocarina / pom / base.py: 100.00%
7 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-17 19:20 +0200
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-17 19:20 +0200
1"""Page Object Model base class.
3This module defines POMBase, the universal abstract base class for all
4page objects, independent of browser automation framework.
6The POM pattern encapsulates:
7- Page verification (verify() method)
8- Page actions (methods to interact with elements)
9- Page elements (locators, element references)
11POMBase provides a minimal, framework-agnostic interface that works with
12Selenium, Playwright, Puppeteer, or any other browser automation tool.
14Example:
15 >>> class LoginPage(POMBase):
16 ... def __init__(self, driver: WebDriver):
17 ... self._driver = driver
18 ...
19 ... def verify(self, timeout: float | None = None) -> Self:
20 ... WebDriverWait(self._driver, timeout or 10).until(
21 ... EC.presence_of_element_located((By.ID, "login-form"))
22 ... )
23 ... return self
24 ...
25 ... def enter_username(self, username: str) -> Self:
26 ... self._driver.find_element(By.ID, "username").send_keys(username)
27 ... return self
29"""
31from abc import ABC, abstractmethod
32from typing import Self
35class POMBase(ABC):
36 """Abstract base class for Page Object Model.
38 Defines the universal contract all page objects must follow, regardless
39 of the underlying browser automation framework (Selenium, Playwright, etc.).
41 The only required method is verify(), which checks that the browser is
42 on the expected page. All other page-specific functionality (actions,
43 element access, etc.) is left to concrete implementations.
45 Subclassing guidelines:
46 - Implement verify() with page-specific checks
47 - Return Self from methods for fluent API
48 - Raise appropriate exceptions on verification failure
49 - Store driver/page instance as needed by framework
51 Example:
52 >>> # Selenium
53 ... class LoginPage(POMBase):
54 ... def __init__(self, driver: WebDriver):
55 ... self._driver = driver
56 ...
57 ... def verify(self, timeout: float | None = None) -> Self:
58 ... WebDriverWait(self._driver, timeout or 10).until(
59 ... EC.presence_of_element_located((By.ID, "login-form"))
60 ... )
61 ... return self
63 Example:
64 >>> # Playwright
65 ... class LoginPage(POMBase):
66 ... def __init__(self, page: Page):
67 ... self._page = page
68 ...
69 ... def verify(self, timeout: float | None = None) -> Self:
70 ... self._page.wait_for_selector("#login-form", timeout=timeout)
71 ... return self
73 """
75 @abstractmethod
76 def verify(self, *, timeout: float | None = None) -> Self:
77 """Verify that the browser is on the expected page.
79 Checks for page-specific indicators such as unique elements, URL
80 patterns, or page content. This method is typically called after
81 navigation or page transitions to confirm the correct page loaded.
83 Args:
84 timeout: Maximum seconds to wait for verification. If None,
85 implementations should use a sensible default (typically 10s).
87 Returns:
88 Self for method chaining.
90 Raises:
91 Implementation-specific exceptions when verification fails
92 (e.g., TimeoutException, PageVerificationError).
94 Example:
95 >>> # Checking for unique element
96 ... def verify(self, timeout: float | None = None) -> Self:
97 ... WebDriverWait(self._driver, timeout or 10).until(
98 ... EC.presence_of_element_located((By.ID, "unique-element"))
99 ... )
100 ... return self
102 Example:
103 >>> # Checking URL pattern
104 ... def verify(self, timeout: float | None = None) -> Self:
105 ... if "/login" not in self._driver.current_url:
106 ... raise PageVerificationError("Not on login page")
107 ... return self
109 """
110 ...
112 @abstractmethod
113 def get_current_title(self) -> str:
114 """Get the current page title from the browser.
116 Returns the text content of the page's <title> element.
117 Used for:
118 - Page verification (check title contains expected text)
119 - Logging (include title in error messages)
120 - Debugging (identify which page we're on)
122 Returns:
123 str: The page title as a string.
124 Empty string if no title element exists.
126 Example (manual implementation):
127 >>> def get_current_title(self) -> str:
128 ... return self._driver.title
130 Example (using in verification):
131 >>> def verify(self, timeout: float | None = None) -> Self:
132 ... if "Login" not in self.get_current_title():
133 ... raise PageVerificationError("Not on login page")
134 ... return self
136 Example (using in logging):
137 >>> def log_current_page(self) -> None:
138 ... logger.info(f"Current page: {self.get_current_title()}")
140 Note:
141 Most implementations should use SeleniumTitleMixin instead of
142 implementing this manually. The mixin provides a standard
143 implementation that simply returns self._driver.title.
145 See Also:
146 - SeleniumTitleMixin: Provides default implementation
148 """
149 ...