diff --git a/src/widgetastic/browser.py b/src/widgetastic/browser.py index dddf89f4..975afba9 100644 --- a/src/widgetastic/browser.py +++ b/src/widgetastic/browser.py @@ -583,3 +583,42 @@ def handle_alert(self, cancel=False, wait=30.0, squash=False, prompt=None, check return False else: raise + + +class BrowserParentWrapper(object): + """A wrapper/proxy class that ensures passing of correct parent locator on elements lookup. + + Required for the proper operation of nesting. + + Assumes the object passed has a ``browser`` attribute. + + Args: + o: Object which should be considered as a parent element for lookups. Must have ``.browser`` + defined. + """ + def __init__(self, o, browser): + self._o = o + self._browser = browser + + def __eq__(self, other): + if not isinstance(other, BrowserParentWrapper): + return False + return self._o == other._o and self._browser == other._browser + + def elements(self, locator, parent=None, check_visibility=False, check_safe=True, + force_check_safe=False): + if parent is None: + parent = self._o + return self._browser.elements( + locator, + parent=parent, + check_visibility=check_visibility, + check_safe=check_safe, + force_check_safe=force_check_safe) + + def __getattr__(self, attr): + """Route all other attribute requests into the parent object's browser.""" + return getattr(self._browser, attr) + + def __repr__(self): + return '<{} for {!r}>'.format(type(self).__name__, self._o) diff --git a/src/widgetastic/widget.py b/src/widgetastic/widget.py index 2c94d34c..f91b56c8 100644 --- a/src/widgetastic/widget.py +++ b/src/widgetastic/widget.py @@ -13,7 +13,7 @@ from smartloc import Locator from wait_for import wait_for -from .browser import Browser +from .browser import Browser, BrowserParentWrapper from .exceptions import ( NoSuchElementException, LocatorNotImplemented, WidgetOperationFailed, DoNotReadThisWidget) from .log import PrependParentsAdapter, create_widget_logger, logged @@ -222,7 +222,12 @@ def browser(self): :py:class:`ValueError` when the browser is not defined, which is an error. """ try: - return self.parent.browser + if hasattr(self, '__locator__'): + # Wrap it so we have automatic parent injection + return BrowserParentWrapper(self, self.parent.browser) + else: + # This view has no locator, therefore just use the parent browser + return self.parent.browser except AttributeError: raise ValueError('Unknown value {!r} specified as parent.'.format(self.parent)) diff --git a/testing/test_browser.py b/testing/test_browser.py index 6134525a..f07745f3 100644 --- a/testing/test_browser.py +++ b/testing/test_browser.py @@ -2,7 +2,9 @@ from __future__ import unicode_literals import pytest +from widgetastic.browser import BrowserParentWrapper from widgetastic.exceptions import NoSuchElementException, LocatorNotImplemented +from widgetastic.widget import View, Text def test_is_displayed(browser): @@ -119,3 +121,42 @@ def test_simple_input_send_keys_clear(browser): assert browser.get_attribute('value', '#input') == 'test!' browser.clear('#input') assert browser.get_attribute('value', '#input') == '' + + +def test_nested_views_parent_injection(browser): + class MyView(View): + ROOT = '#proper' + + class c1(View): # noqa + ROOT = '.c1' + + w = Text('.lookmeup') + + class c2(View): # noqa + ROOT = '.c2' + + w = Text('.lookmeup') + + class c3(View): # noqa + ROOT = '.c3' + + w = Text('.lookmeup') + + class without(View): # noqa + # This one receives the parent browser wrapper + class nested(View): # noqa + # and it should work in multiple levels + pass + + view = MyView(browser) + assert isinstance(view.browser, BrowserParentWrapper) + assert view.browser == view.without.browser + assert view.browser == view.without.nested.browser + assert len(view.c1.browser.elements('.lookmeup')) == 1 + assert view.c1.w.text == 'C1' + assert len(view.c2.browser.elements('.lookmeup')) == 1 + assert view.c2.w.text == 'C2' + assert len(view.c3.browser.elements('.lookmeup')) == 1 + assert view.c3.w.text == 'C3' + + assert len(view.browser.elements('.lookmeup')) == 3 diff --git a/testing/testing_page.html b/testing/testing_page.html index 79285b05..30c53835 100644 --- a/testing/testing_page.html +++ b/testing/testing_page.html @@ -71,5 +71,19 @@

test test

+
+ BAD +
+
+
+ C1 +
+
+ C2 +
+
+ C3 +
+