Coverage for src / ocarina / pom / base.py: 100.00%

7 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-31 10:31 +0200

1"""Page Object Model base class. 

2 

3This module defines POMBase, the universal abstract base class for all 

4page objects, independent of browser automation framework. 

5 

6The POM pattern encapsulates: 

7- Page verification (verify() method) 

8- Page actions (methods to interact with elements) 

9- Page elements (locators, element references) 

10 

11POMBase provides a minimal, framework-agnostic interface that works with 

12Selenium, Playwright, Puppeteer, or any other browser automation tool. 

13 

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 

28 

29""" 

30 

31from abc import ABC, abstractmethod 

32from typing import Self 

33 

34 

35class POMBase(ABC): 

36 """Abstract base class for Page Object Model. 

37 

38 Defines the universal contract all page objects must follow, regardless 

39 of the underlying browser automation framework (Selenium, Playwright, etc.). 

40 

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. 

44 

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 

50 

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 

62 

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 

72 

73 """ 

74 

75 @abstractmethod 

76 def verify(self, *, timeout: float | None = None) -> Self: 

77 """Verify that the browser is on the expected page. 

78 

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. 

82 

83 Args: 

84 timeout: Maximum seconds to wait for verification. If None, 

85 implementations should use a sensible default (typically 10s). 

86 

87 Returns: 

88 Self for method chaining. 

89 

90 Raises: 

91 Implementation-specific exceptions when verification fails 

92 (e.g., TimeoutException, PageVerificationError). 

93 

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 

101 

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 

108 

109 """ 

110 ... 

111 

112 @abstractmethod 

113 def get_current_title(self) -> str: 

114 """Get the current page title from the browser. 

115 

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) 

121 

122 Returns: 

123 str: The page title as a string. 

124 Empty string if no title element exists. 

125 

126 Example (manual implementation): 

127 >>> def get_current_title(self) -> str: 

128 ... return self._driver.title 

129 

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 

135 

136 Example (using in logging): 

137 >>> def log_current_page(self) -> None: 

138 ... logger.info(f"Current page: {self.get_current_title()}") 

139 

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. 

144 

145 See Also: 

146 - SeleniumTitleMixin: Provides default implementation 

147 

148 """ 

149 ...