Skip to content

Commit

Permalink
Merge 9b1f11a into e7a0bea
Browse files Browse the repository at this point in the history
  • Loading branch information
izapolsk committed May 6, 2019
2 parents e7a0bea + 9b1f11a commit 2e7b12b
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 9 deletions.
35 changes: 34 additions & 1 deletion docs/advanced_usage.rst
Expand Up @@ -277,4 +277,37 @@ also make a switchable widget. You can use it like this:
switched_widget.register('Action type 1', default=True, widget=Widget())
Then instead of switching views, it switches widgets.
Then instead of switching views, it switches widgets.

IFrame support is views
-----------------------------

If some html page has embedded iframes, those can be covered using regular view.
You just need to set FRAME property for it. FRAME should point out to appropriate iframe and can be xpath and whatever supported by widgetastic.

Since iframe is another page, all its bits consider iframe as root. This has to be taken into account during creating object structure.

If regular views and iframe views are mixed, widgetastic takes care of switching between frames on widget access.
User doesn't need to undertake any actions.

Below is example of usage. More examples can be found in unit tests.

.. code-block:: python
class FirstIFrameView(View):
FRAME = '//iframe[@name="some_iframe"]'
h3 = Text('.//h3')
select1 = Select(id='iframe_select1')
select2 = Select(name='iframe_select2')
class RegularView(View):
h3 = Text('//h3[@id="someid-1"]')
checkbox1 = Checkbox(id='checkbox-1')
class SecondIFrameView(View):
FRAME = './/iframe[@name="another_iframe"]'
widget1 = Widget()
widget2 = Widget()
2 changes: 2 additions & 0 deletions setup.py
Expand Up @@ -18,6 +18,8 @@
'anytree',
'cached_property',
'jsmin',
'pytest',
'selenium',
'selenium-smart-locator',
'six',
'wait_for',
Expand Down
11 changes: 11 additions & 0 deletions src/widgetastic/browser.py
Expand Up @@ -804,6 +804,17 @@ def handle_alert(self, cancel=False, wait=30.0, squash=False, prompt=None, check
else:
raise

def switch_to_frame(self, *args, **kwargs):
parent = kwargs.pop('parent', self.browser)
self.selenium.switch_to.frame(self.element(parent=parent, *args, **kwargs))

def switch_to_main_frame(self):
self.selenium.switch_to.default_content()

def get_current_location(self):
# useful if it is necessary to recognize current frame
return self.execute_script('return self.location.toString()')


class BrowserParentWrapper(object):
"""A wrapper/proxy class that ensures passing of correct parent locator on elements lookup.
Expand Down
14 changes: 14 additions & 0 deletions src/widgetastic/widget/base.py
Expand Up @@ -874,6 +874,7 @@ def a_method(self):
"""
#: Skip this view in the element lookup hierarchy
INDIRECT = False
FRAME = None
fill_strategy = None

def __init__(self, parent, logger=None, **kwargs):
Expand Down Expand Up @@ -1015,6 +1016,19 @@ def after_fill(self, was_change):
"""
pass

def child_widget_accessed(self, widget):
"""This hook is called when a child widget of current view is accessed.
One of useful examples is below. it allows us to switch between frames.
Args:
widget: The widget being accessed.
"""
self.browser.switch_to_main_frame()
parents = [p for p in self.hierarchy if getattr(p, 'FRAME', None)]
for parent in parents:
self.browser.switch_to_frame(getattr(parent, 'FRAME'))


class ParametrizedView(View):
"""View that needs parameters to be run.
Expand Down
57 changes: 51 additions & 6 deletions testing/conftest.py
Expand Up @@ -6,8 +6,10 @@
import os
import sys

from pytest_localserver.http import ContentServer, Request, Response
from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from six.moves.urllib_parse import urlsplit

from widgetastic.browser import Browser

Expand All @@ -32,12 +34,55 @@ def selenium(request):
return driver


@pytest.fixture(scope='module')
def test_server(request):
class TestContentServer(ContentServer):

def __init__(self, *args, **kwargs):
this_module = sys.modules[__name__]
self.path = os.path.join(os.path.dirname(this_module.__file__), 'html')
super(TestContentServer, self).__init__(*args, **kwargs)

def __call__(self, environ, start_response):
"""
This is the WSGI application.
"""
request = Request(environ)
self.requests.append(request)

code = 200
if request.url.endswith('/'):
file = os.path.join(self.path, 'testing_page.html')
elif request.url.endswith('.html'):
url_path = urlsplit(request.url).path
filename = os.path.split(url_path)[-1]
file = os.path.join(self.path, filename)
else:
file = ''
code = 404

if os.path.exists(file):
content = codecs.open(file, mode='r', encoding='utf-8').read()
else:
content = "wrong url {}".format(request.url)
code = 404

response = Response(status=code)
response.headers.clear()
response.headers.extend(self.headers)

response.data = content
return response(environ, start_response)

server = TestContentServer()
server.start()
request.addfinalizer(server.stop)
return server


@pytest.fixture(scope='function')
def browser(selenium, httpserver, request):
this_module = sys.modules[__name__]
path = os.path.dirname(this_module.__file__)
testfilename = os.path.join(path, 'testing_page.html')
httpserver.serve_content(codecs.open(testfilename, mode='r', encoding='utf-8').read())
def browser(selenium, test_server):

b = CustomBrowser(selenium)
b.url = httpserver.url
b.url = test_server.url
return b
22 changes: 22 additions & 0 deletions testing/html/iframe_page.html
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page for testing IFrames</title>
</head>
<body>
<h3>IFrame Tests</h3>

<select id="iframe_select1">
<option value="foo">Foo</option>
<option value="bar"> Bar</option>
</select>

<select name="iframe_select2" multiple class="xfoo xbar">
<option value="foo">Foo</option>
<option value="bar"> Bar</option>
<option value="baz">Baz</option>
</select>
<iframe src="iframe_page2.html" height="300" width="200" name="another_iframe"></iframe>
</body>
</html>
18 changes: 18 additions & 0 deletions testing/html/iframe_page2.html
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page for testing IFrames 2</title>
</head>
<body>
<h3>IFrame Tests 2</h3>

<select id="iframe_select3">
<option value="foo">Foo</option>
<option value="bar"> Bar</option>
</select>
<div id="nested_view">
<input name="input222" type="text" value="Default Value">
</div>
</body>
</html>
Expand Up @@ -286,6 +286,6 @@ <h3 id="switchabletesting-2">bartest</h3>
</tr>
</tbody>
</table>

<iframe src="iframe_page.html" height="600" width="800" name="some_iframe"></iframe>
</body>
</html>
50 changes: 50 additions & 0 deletions testing/test_view.py
Expand Up @@ -672,3 +672,53 @@ class MyView(View):
assert 'baz' not in view.widget_names
assert 'inga' in view.widget_names
assert isinstance(view.inga, MyViewToNotNest)


def test_iframe_view(browser):
class MyIFrameView(View):
FRAME = '//iframe[@name="some_iframe"]'
h3 = Text('.//h3')
select1 = Select(id='iframe_select1')
select2 = Select(name='iframe_select2')

class nested_iframe_view(View):
FRAME = './/iframe[@name="another_iframe"]'
h3 = Text('.//h3')
select3 = Select(id='iframe_select3')

class nested(View):
ROOT = './/div[@id="nested_view"]'
nested_input = TextInput(name='input222')

class ParentView(View):
h3 = Text('//h3[@id="switchabletesting-1"]')
checkbox1 = Checkbox(id='switchabletesting-3')
checkbox2 = Checkbox(id='switchabletesting-4')

iframe_view = MyIFrameView(browser)
parent_view = ParentView(browser)

assert iframe_view.is_displayed
assert parent_view.is_displayed

assert all([getattr(iframe_view, name).is_displayed for name in iframe_view.widget_names])
assert all([getattr(parent_view, name).is_displayed for name in parent_view.widget_names])

assert iframe_view.h3.text == "IFrame Tests"
assert parent_view.h3.text == "footest"

assert iframe_view.select1.read() == 'Foo'
assert iframe_view.select1.fill('Bar') and iframe_view.select1.read() == 'Bar'

assert not parent_view.checkbox1.read()
assert parent_view.checkbox1.fill(True) and parent_view.checkbox1.read()

assert iframe_view.nested_iframe_view.is_displayed
assert iframe_view.nested_iframe_view.h3.text == 'IFrame Tests 2'
assert (iframe_view.nested_iframe_view.select3.fill('Bar') and
iframe_view.nested_iframe_view.select3.read() == 'Bar')

assert iframe_view.nested_iframe_view.nested.is_displayed
assert iframe_view.nested_iframe_view.nested.nested_input.read() == 'Default Value'
assert iframe_view.nested_iframe_view.nested.nested_input.fill('New Value')
assert iframe_view.nested_iframe_view.nested.nested_input.read() == 'New Value'
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -9,7 +9,7 @@ deps=
pytest-cov==2.6.0
anytree
commands =
py.test {posargs: -v --cov widgetastic --cov-report term-missing}
py.test {posargs: -v --cov widgetastic --cov-report term-missing -s}

[testenv:codechecks]
skip_install = true
Expand Down

0 comments on commit 2e7b12b

Please sign in to comment.