Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/widgetastic/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from .exceptions import (
NoSuchElementException, UnexpectedAlertPresentException, MoveTargetOutOfBoundsException,
StaleElementReferenceException, NoAlertPresentException, LocatorNotImplemented)
StaleElementReferenceException, NoAlertPresentException, LocatorNotImplemented, WidgetNotFound)
from .log import create_widget_logger, null_logger
from .xpath import normalize_space
from .utils import crop_string_middle
Expand Down Expand Up @@ -417,7 +417,7 @@ def is_displayed(self, locator, *args, **kwargs):
retry = False
try:
return self.move_to_element(locator, *args, **kwargs).is_displayed()
except (NoSuchElementException, MoveTargetOutOfBoundsException):
except (NoSuchElementException, MoveTargetOutOfBoundsException, WidgetNotFound):
return False
except StaleElementReferenceException:
if isinstance(locator, WebElement) or tries <= 0:
Expand Down
26 changes: 22 additions & 4 deletions src/widgetastic/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,35 @@
NoAlertPresentException, UnexpectedAlertPresentException) # NOQA


class LocatorNotImplemented(NotImplementedError):
class WidgetasticException(Exception):
pass


class WidgetOperationFailed(Exception):
class LocatorNotImplemented(NotImplementedError, WidgetasticException):
pass


class DoNotReadThisWidget(Exception):
class WidgetOperationFailed(WidgetasticException):
pass


class RowNotFound(IndexError):
class DoNotReadThisWidget(WidgetasticException):
pass


class RowNotFound(IndexError, WidgetasticException):
pass


class WidgetNotFound(WidgetasticException):
"""Raised when a widget was not found"""
def __init__(self, widget, original_exception, widget_path):
self.widget = widget
self.original_exception = original_exception
self.widget_path = widget_path

def get_message(self):
return 'Widget {} not found'.format('/'.join(self.widget_path))

def __str__(self):
return self.get_message()
40 changes: 32 additions & 8 deletions src/widgetastic/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from .browser import Browser, BrowserParentWrapper
from .exceptions import (
NoSuchElementException, LocatorNotImplemented, WidgetOperationFailed, DoNotReadThisWidget,
RowNotFound)
RowNotFound, WidgetNotFound)
from .log import (
PrependParentsAdapter, create_widget_logger, logged, call_sig, create_child_logger,
create_item_logger)
Expand Down Expand Up @@ -282,16 +282,24 @@ def __init__(self, parent, arg1, arg2, logger=None):
self._initialized_included_widgets = {}

def __element__(self):
"""Looks up the element in parent browser based on what ``__locator__`` returns.

Raises:
:py:class:`widgetastic.exceptions.WidgetNotFound`
"""
try:
locator = self.__locator__()
except AttributeError:
raise AttributeError(
'__locator__() is not defined on {} class'.format(type(self).__name__))
else:
if isinstance(locator, WebElement):
return locator
else:
return self.parent_browser.element(locator)

if isinstance(locator, WebElement):
return locator

try:
return self.parent_browser.element(locator)
except NoSuchElementException as e:
raise WidgetNotFound(self, e, self.widget_names_path)

def _get_included_widget(self, includer_id, widget_name, use_parent):
if includer_id not in self._initialized_included_widgets:
Expand Down Expand Up @@ -570,6 +578,22 @@ def __iter__(self):
for widget_attr in self.widget_names:
yield getattr(self, widget_attr)

@property
def widget_name(self):
"""Returns this widget's name as defined on parent widget."""
try:
return self.parent._desc_name_mapping[self.parent_descriptor]
except AttributeError:
return type(self).__name__

@property
def widget_names_path(self):
"""Returns a list of the widget hierarchy, referenced by names."""
if not isinstance(self.parent, Widget):
return [self.widget_name]
else:
return self.parent.widget_names_path + [self.widget_name]


def _gen_locator_meth(loc):
def __locator__(self): # noqa
Expand Down Expand Up @@ -730,7 +754,7 @@ def read(self):
widget = getattr(self, widget_name)
try:
value = widget.read()
except (NotImplementedError, NoSuchElementException, DoNotReadThisWidget):
except (NotImplementedError, WidgetNotFound, DoNotReadThisWidget):
continue

result[widget_name] = value
Expand Down Expand Up @@ -2169,7 +2193,7 @@ def __get__(self, o, t):
raise TypeError(
'Wrong widget name specified as reference=: {}'.format(
self.reference))
except NoSuchElementException:
except WidgetNotFound:
if self.ignore_bad_reference:
# reference is not displayed? We are probably aware of this so skip.
continue
Expand Down
4 changes: 2 additions & 2 deletions testing/test_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import unicode_literals

import pytest
from widgetastic.exceptions import NoSuchElementException
from widgetastic.exceptions import WidgetNotFound
from widgetastic.utils import ParametrizedLocator, ParametrizedString, Parameter, Ignore
from widgetastic.widget import (
ParametrizedView, ParametrizedViewRequest, Text, View, Widget, do_not_read_this_widget,
Expand Down Expand Up @@ -411,7 +411,7 @@ class BarView(View):

view = MyView(browser)

with pytest.raises(NoSuchElementException):
with pytest.raises(WidgetNotFound):
view.the_switchable_view.widget.read()


Expand Down
38 changes: 37 additions & 1 deletion testing/test_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from __future__ import absolute_import
import pytest

from widgetastic.widget import View, Widget, WidgetDescriptor
from widgetastic.exceptions import WidgetNotFound, NoSuchElementException
from widgetastic.widget import View, Widget, WidgetDescriptor, Text


def test_widget_correctly_collapses_to_descriptor(browser):
Expand Down Expand Up @@ -93,3 +94,38 @@ class MyClass4(Widget):
assert testw.beef.id == 'beef'
assert isinstance(testw.bob, MyWidget)
assert testw.bob.id == 'bob'


def test_widget_name(browser):
widget_without_parent = Widget(browser)

assert widget_without_parent.widget_name == 'Widget'
assert widget_without_parent.widget_names_path == ['Widget']

class AHostView1(View):
named_widget = Widget()

view = AHostView1(browser)
assert view.named_widget.widget_name == 'named_widget'
assert view.named_widget.widget_names_path == ['AHostView1', 'named_widget']

class ANestedView1(View):
class something_else(View): # NOQA
another_widget = Text('#doesnotexist')

view = ANestedView1(browser)
assert view.something_else.widget_name == 'something_else'
assert view.something_else.widget_names_path == ['ANestedView1', 'something_else']
assert view.something_else.another_widget.widget_name == 'another_widget'
assert view.something_else.another_widget.widget_names_path == [
'ANestedView1', 'something_else', 'another_widget']

try:
view.something_else.another_widget.read()
except WidgetNotFound as e:
assert e.widget is view.something_else.another_widget
assert e.widget_path == ['ANestedView1', 'something_else', 'another_widget']
assert isinstance(e.original_exception, NoSuchElementException)
assert e.get_message() == 'Widget ANestedView1/something_else/another_widget not found'
else:
pytest.fail('Exception WidgetNotFound was not raised')