pomcorn 0.8.3__py3-none-any.whl → 0.8.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pomcorn might be problematic. Click here for more details.

pomcorn/component.py CHANGED
@@ -352,7 +352,9 @@ class ListComponent(Generic[ListItemType, TPage], Component[TPage]):
352
352
  def get_item_by_text(self, text: str) -> ListItemType:
353
353
  """Get list item by text."""
354
354
  locator = self.base_item_locator.extend_query(
355
- extra_query=f"[contains(.,'{text}')]",
355
+ extra_query=(
356
+ f"[contains(., {self.base_item_locator._escape_quotes(text)})]"
357
+ ),
356
358
  )
357
359
  return self._item_class(page=self.page, base_locator=locator)
358
360
 
@@ -156,6 +156,47 @@ class XPathLocator(Locator):
156
156
  """Return whether query of current locator is empty or not."""
157
157
  return bool(self.related_query)
158
158
 
159
+ @classmethod
160
+ def _escape_quotes(cls, text: str) -> str:
161
+ """Escape single and double quotes in given text for use in locators. # noqa: D202, E501.
162
+
163
+ This method is useful when locating elements
164
+ with text containing single or double quotes.
165
+
166
+ For example, the text `He's 6'2"` will be transformed into:
167
+ `concat("He", "'", "s 6", "'", "2", '"')`.
168
+
169
+ The resulting string can be used in XPath expressions
170
+ like `text()=...` or `contains(.,...)`.
171
+
172
+ Returns:
173
+ The escaped text wrapped in `concat()` for XPath compatibility,
174
+ or the original text in double quotes if no escaping is needed.
175
+
176
+ """
177
+
178
+ if not text or ('"' not in text and "'" not in text):
179
+ return f'"{text}"'
180
+
181
+ escaped_parts = []
182
+ buffer = "" # Temporary storage for normal characters
183
+
184
+ for char in text:
185
+ if char not in ('"', "'"):
186
+ buffer += char
187
+ continue
188
+ if buffer:
189
+ escaped_parts.append(f'"{buffer}"')
190
+ buffer = ""
191
+ escaped_parts.append(
192
+ "'" + char + "'" if char == '"' else '"' + char + '"',
193
+ )
194
+
195
+ if buffer:
196
+ escaped_parts.append(f'"{buffer}"')
197
+
198
+ return f"concat({', '.join(escaped_parts)})"
199
+
159
200
  def extend_query(self, extra_query: str) -> XPathLocator:
160
201
  """Return new XPathLocator with extended query."""
161
202
  return XPathLocator(query=self.query + extra_query)
@@ -172,8 +213,8 @@ class XPathLocator(Locator):
172
213
  By default, the search is based on a partial match.
173
214
 
174
215
  """
175
- partial_query = f"[contains(., '{text}')]"
176
- exact_query = f"[./text()='{text}']"
216
+ partial_query = f"[contains(., {self._escape_quotes(text)})]"
217
+ exact_query = f"[./text()={self._escape_quotes(text)}]"
177
218
  return self.extend_query(exact_query if exact else partial_query)
178
219
 
179
220
  def prepare_relative_locator(
@@ -140,8 +140,8 @@ class ElementWithTextLocator(XPathLocator):
140
140
  partial match of the value.
141
141
 
142
142
  """
143
- exact_query = f'//{element}[./text()="{text}"]'
144
- partial_query = f'//{element}[contains(.,"{text}")]'
143
+ exact_query = f"//{element}[./text()={self._escape_quotes(text)}]"
144
+ partial_query = f"//{element}[contains(.,{self._escape_quotes(text)})]"
145
145
 
146
146
  super().__init__(query=exact_query if exact else partial_query)
147
147
 
@@ -219,7 +219,7 @@ class InputInLabelLocator(XPathLocator):
219
219
  def __init__(self, label: str):
220
220
  """Init XPathLocator."""
221
221
  super().__init__(
222
- query=f'//label[contains(., "{label}")]//input',
222
+ query=f"//label[contains(., {self._escape_quotes(label)})]//input",
223
223
  )
224
224
 
225
225
 
@@ -243,7 +243,10 @@ class InputByLabelLocator(XPathLocator):
243
243
  def __init__(self, label: str):
244
244
  """Init XPathLocator."""
245
245
  super().__init__(
246
- query=f'//label[contains(., "{label}")]/following-sibling::input',
246
+ query=(
247
+ f"//label[contains(., {self._escape_quotes(label)})]"
248
+ "/following-sibling::input"
249
+ ),
247
250
  )
248
251
 
249
252
 
@@ -259,5 +262,8 @@ class TextAreaByLabelLocator(XPathLocator):
259
262
  def __init__(self, label: str):
260
263
  """Init XPathLocator."""
261
264
  super().__init__(
262
- query=f'//*[label[contains(text(), "{label}")]]/textarea',
265
+ query=(
266
+ "//*[label[contains(text(), "
267
+ f"{self._escape_quotes(label)})]]/textarea"
268
+ ),
263
269
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pomcorn
3
- Version: 0.8.3
3
+ Version: 0.8.4
4
4
  Summary: Base implementation of Page Object Model
5
5
  Home-page: https://pypi.org/project/pomcorn/
6
6
  License: MIT
@@ -1,17 +1,17 @@
1
1
  pomcorn/__init__.py,sha256=V8v_V3wgKbao1KAQkcVTMnpGwyslw0nHfuTp-DwWTXg,318
2
- pomcorn/component.py,sha256=vdt7sEmQodlVUTRDeAcqeGa37MpDwCZSv8TYHQycAgs,12094
2
+ pomcorn/component.py,sha256=XdG6OJ_4dKXsDh4E_TJhbmcL3iXFOI8lvopCVjWpwXM,12164
3
3
  pomcorn/descriptors/__init__.py,sha256=5q_d5GtcaFlIIyHdsUnE9UNbz3g40qJCHKNaWGvcC6s,72
4
4
  pomcorn/descriptors/element.py,sha256=4g_4ay5f00AXzCT4xHN1i7mTrlsECoUHh1Ph_TFPBDs,4502
5
5
  pomcorn/element.py,sha256=6VLLSPlc7961qa4rRXdno7cEAAeY8GEP2d4wtM5R9a0,12216
6
6
  pomcorn/exceptions.py,sha256=8ZOmrybDwvEVZRLQzyozjRNhJvSLbablaFwGFUVMhrU,1131
7
7
  pomcorn/locators/__init__.py,sha256=hNSlfmz3y-LI4PXF5VIwX8J5WSalyE2wmzuYc6kNxMA,708
8
- pomcorn/locators/base_locators.py,sha256=B4SDSKi5j7kqwbxllnMHU9BKBJzn_dzNRphPQ2hTuss,7065
9
- pomcorn/locators/xpath_locators.py,sha256=OjwTdHwqDfX_5aP0N6YeFwRl2gPr8ZNQny8yLxBrVCo,7264
8
+ pomcorn/locators/base_locators.py,sha256=nokiwxnIQh90bolOJcFJSQjuaX_0a4iB5aPhp_Vrbr8,8439
9
+ pomcorn/locators/xpath_locators.py,sha256=zcGNIk5HX36TTauCApgX0csdobWj8QFZ_td4-9qkTgU,7461
10
10
  pomcorn/page.py,sha256=wuLoGUn1n0e7wSXLGYyUVnoPlz_gMfT8Jtg6yA09IeY,5755
11
11
  pomcorn/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  pomcorn/waits_conditions.py,sha256=E8sLfBvIV2EO91Mm5KyKXB5IGEB9gUWza3XPE4_aCQE,3528
13
13
  pomcorn/web_view.py,sha256=PlwEVgdOwFZXJY2WuUT2C2wyK1xOBJne5-A_FOuwsQI,14209
14
- pomcorn-0.8.3.dist-info/LICENSE,sha256=1vmhQNp1dDH8ksJ199LuG4oKz5D4ZvNccPcFckiG2S4,1091
15
- pomcorn-0.8.3.dist-info/METADATA,sha256=jwKTfVeunUJii4W2AS8BWLXrqwCnD-y0GCEe4XJgmpc,5973
16
- pomcorn-0.8.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
- pomcorn-0.8.3.dist-info/RECORD,,
14
+ pomcorn-0.8.4.dist-info/LICENSE,sha256=1vmhQNp1dDH8ksJ199LuG4oKz5D4ZvNccPcFckiG2S4,1091
15
+ pomcorn-0.8.4.dist-info/METADATA,sha256=jPDp_4lyHdfibn0IqP1DNHb0ZP5QhG55dkA8aEWDZoE,5973
16
+ pomcorn-0.8.4.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
+ pomcorn-0.8.4.dist-info/RECORD,,