Skip to content

Demo Mode for SeleniumBase #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 17, 2016
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -199,11 +199,25 @@ nosetests my_first_test.py --browser=firefox --with-selenium -s
```

After the test completes, in the console output you'll see a dot on a new line, representing a passing test. (On test failures you'll see an F instead, and on test errors you'll see an E). It looks more like a moving progress bar when you're running a ton of unit tests side by side. This is part of nosetests. After all tests complete (in this case there is only one), you'll see the "Ran 1 test in ..." line, followed by an "OK" if all nosetests passed.
If the example is moving too fast for your eyes to see what's going on, there are 2 things you can do. Add either of the following:

If the example is moving too fast for your eyes to see what's going on, there are a few things you can do.
You can add ``--demo_mode`` on the command line, which pauses the browser for about a second (by default) after each action:

```bash
nosetests my_first_test.py --with-selenium -s --demo_mode
```

You can override the default wait time by either updating [settings.py](https://github.com/mdmintz/SeleniumBase/blob/master/seleniumbase/config/settings.py) or by using ``--demo_sleep={NUM}`` when using Demo Mode. (NOTE: If you use ``--demo_sleep={NUM}`` without using ``--demo_mode``, nothing will happen.)

```bash
nosetests my_first_test.py --with-selenium -s --demo_mode --demo_sleep=1.2
```

You can also add either of the following to your scripts to slow down the tests:

```python
import time; time.sleep(5) # sleep for 5 seconds (add this after the line you want to pause on)
import ipdb; ipdb.set_trace() # waits for your command. n = next line of current method, c = continue, s = step / next executed line (will jump)
import time; time.sleep(5) # sleep for 5 seconds (add this after the line you want to pause on)
import ipdb; ipdb.set_trace() # waits for your command. n = next line of current method, c = continue, s = step / next executed line (will jump)
```

(NOTE: If you're using pytest instead of nosetests and you want to use ipdb in your script for debugging purposes, you'll either need to add "--capture=no" on the command line, or use "import pytest; pytest.set_trace()" instead of using ipdb. More info on that [here](http://stackoverflow.com/questions/2678792/can-i-debug-with-python-debugger-when-using-py-test-somehow).)
15 changes: 15 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -34,14 +34,27 @@ def pytest_addoption(parser):
parser.addoption('--log_path', dest='log_path',
default='logs/',
help='Where the log files are saved.')
parser.addoption('--demo_mode', action="store_true",
dest='demo_mode',
default=False,
help="""Using this slows down the automation so that
you can see what it's actually doing.""")
parser.addoption('--demo_sleep', action='store', dest='demo_sleep',
default=None,
help="""Setting this overrides the Demo Mode sleep
time that happens after browser actions.""")


def pytest_configure(config):
with_selenium = config.getoption('with_selenium')
with_testing_base = config.getoption('with_testing_base')
browser = config.getoption('browser')
log_path = config.getoption('log_path')
demo_mode = config.getoption('demo_mode')
demo_sleep = ''
data = ''
if config.getoption('demo_sleep') is not None:
demo_sleep = config.getoption('demo_sleep')
if config.getoption('data') is not None:
data = config.getoption('data')
# Create a temporary config file while tests are running
@@ -52,6 +65,8 @@ def pytest_configure(config):
config_file.write("data:::%s\n" % data)
config_file.write("with_testing_base:::%s\n" % with_testing_base)
config_file.write("log_path:::%s\n" % log_path)
config_file.write("demo_mode:::%s\n" % demo_mode)
config_file.write("demo_sleep:::%s\n" % demo_sleep)
config_file.close()
log_folder_setup(config)

2 changes: 1 addition & 1 deletion docker/docker_setup.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@

setup(
name='seleniumbase',
version='1.1.15',
version='1.1.16',
author='Michael Mintz',
author_email='@mintzworld',
maintainer='Michael Mintz',
6 changes: 6 additions & 0 deletions seleniumbase/config/settings.py
Original file line number Diff line number Diff line change
@@ -10,6 +10,12 @@
LARGE_TIMEOUT = 10
EXTREME_TIMEOUT = 30

# Default time to wait after each browser action performed during Demo Mode
# Use Demo Mode when you want others to see what your automation is doing
# Usage: --demo_mode when run from the command line when using --with-selenium
# This value can be overwritten on the command line by using --demo_sleep=FLOAT
DEFAULT_DEMO_MODE_TIMEOUT = 1.2

# If True, existing logs from past test runs will be saved and take up space.
# If False, only the logs from the most recent test run will be saved locally.
# This has no effect on Jenkins/S3/MySQL, which may still be saving test logs.
76 changes: 71 additions & 5 deletions seleniumbase/fixtures/base_case.py
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@ def open(self, url):
self.driver.get(url)
if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:
self.wait_for_ready_state_complete()
self._demo_mode_pause_if_active()

def open_url(self, url):
""" In case people are mixing up self.open() with open(),
@@ -48,9 +49,11 @@ def click(self, selector, by=By.CSS_SELECTOR,
timeout=settings.SMALL_TIMEOUT):
element = page_actions.wait_for_element_visible(
self.driver, selector, by, timeout=timeout)
self._demo_mode_scroll_if_active(selector, by)
element.click()
if settings.WAIT_FOR_RSC_ON_CLICKS:
self.wait_for_ready_state_complete()
self._demo_mode_pause_if_active()

def click_chain(self, selectors_list, by=By.CSS_SELECTOR,
timeout=settings.SMALL_TIMEOUT, spacing=0):
@@ -66,12 +69,14 @@ def click_link_text(self, link_text, timeout=settings.SMALL_TIMEOUT):
element.click()
if settings.WAIT_FOR_RSC_ON_CLICKS:
self.wait_for_ready_state_complete()
self._demo_mode_pause_if_active()

def add_text(self, selector, new_value, timeout=settings.SMALL_TIMEOUT):
""" The more-reliable version of driver.send_keys()
Similar to update_text(), but won't clear the text field first. """
element = self.wait_for_element_visible(selector, timeout=timeout)
element.send_keys(new_value)
self._demo_mode_pause_if_active()

def send_keys(self, selector, new_value, timeout=settings.SMALL_TIMEOUT):
""" Same as add_text() -> more reliable, but less name confusion. """
@@ -88,12 +93,14 @@ def update_text_value(self, selector, new_value,
"""
element = self.wait_for_element_visible(selector, timeout=timeout)
element.clear()
self._demo_mode_pause_if_active(tiny=True)
element.send_keys(new_value)
if (retry and element.get_attribute('value') != new_value and (
not new_value.endswith('\n'))):
logging.debug('update_text_value is falling back to jQuery!')
selector = self.jq_format(selector)
self.set_value(selector, new_value)
self._demo_mode_pause_if_active()

def update_text(self, selector, new_value,
timeout=settings.SMALL_TIMEOUT, retry=False):
@@ -124,57 +131,97 @@ def execute_script(self, script):

def set_window_size(self, width, height):
return self.driver.set_window_size(width, height)
self._demo_mode_pause_if_active()

def maximize_window(self):
return self.driver.maximize_window()
self._demo_mode_pause_if_active()

def activate_jquery(self):
""" (It's not on by default on all website pages.) """
""" If "jQuery is not defined", use this method to activate it for use.
This happens because jQuery is not always defined on web sites. """
try:
# Let's first find out if jQuery is already defined.
self.driver.execute_script("jQuery('html')")
# Since that command worked, jQuery is defined. Let's return.
return
except Exception:
# jQuery is not currently defined. Let's proceed by defining it.
pass
self.driver.execute_script(
'''var script = document.createElement("script"); '''
'''script.src = "https://ajax.googleapis.com/ajax/libs/jquery/1/'''
'''jquery.min.js"; document.getElementsByTagName("head")[0]'''
'''.appendChild(script);''')
for x in xrange(30):
# jQuery needs a small amount of time to activate. (At most 3s)
try:
self.driver.execute_script("jQuery('html')")
return
except Exception:
time.sleep(0.1)
# Since jQuery still isn't activating, give up and raise an exception
raise Exception("Exception: WebDriver could not activate jQuery!")

def scroll_to(self, selector):
self.wait_for_element_visible(selector, timeout=settings.SMALL_TIMEOUT)
self.driver.execute_script(
"jQuery('%s')[0].scrollIntoView()" % selector)
scroll_script = "jQuery('%s')[0].scrollIntoView()" % selector
try:
self.driver.execute_script(scroll_script)
except Exception:
# The likely reason this fails is because: "jQuery is not defined"
self.activate_jquery() # It's a good thing we can define it here
self.driver.execute_script(scroll_script)
self._demo_mode_pause_if_active(tiny=True)

def scroll_click(self, selector):
self.scroll_to(selector)
self.click(selector)

def jquery_click(self, selector):
self.scroll_to(selector)
self.driver.execute_script("jQuery('%s').click()" % selector)
self._demo_mode_pause_if_active()

def jq_format(self, code):
return page_utils.jq_format(code)

def set_value(self, selector, value):
self.scroll_to(selector)
val = json.dumps(value)
self.driver.execute_script("jQuery('%s').val(%s)" % (selector, val))
self._demo_mode_pause_if_active()

def jquery_update_text_value(self, selector, new_value,
timeout=settings.SMALL_TIMEOUT):
element = self.wait_for_element_visible(selector, timeout=timeout)
self.scroll_to(selector)
self.driver.execute_script("""jQuery('%s').val('%s')"""
% (selector, self.jq_format(new_value)))
if new_value.endswith('\n'):
element.send_keys('\n')
self._demo_mode_pause_if_active()

def jquery_update_text(self, selector, new_value,
timeout=settings.SMALL_TIMEOUT):
self.jquery_update_text_value(selector, new_value, timeout=timeout)

def hover_on_element(self, selector):
self.wait_for_element_visible(selector, timeout=settings.SMALL_TIMEOUT)
self.scroll_to(selector)
time.sleep(0.05) # Settle down from scrolling before hovering
return page_actions.hover_on_element(self.driver, selector)

def hover_and_click(self, hover_selector, click_selector,
click_by=By.CSS_SELECTOR,
timeout=settings.SMALL_TIMEOUT):
return page_actions.hover_and_click(self.driver, hover_selector,
click_selector, click_by, timeout)
self.wait_for_element_visible(hover_selector, timeout=timeout)
self.scroll_to(hover_selector)
# Settle down from the scrolling before hovering
element = page_actions.hover_and_click(
self.driver, hover_selector, click_selector, click_by, timeout)
self._demo_mode_pause_if_active()
return element

def wait_for_element_present(self, selector, by=By.CSS_SELECTOR,
timeout=settings.LARGE_TIMEOUT):
@@ -221,6 +268,23 @@ def wait_for_and_switch_to_alert(self, timeout=settings.LARGE_TIMEOUT):
def save_screenshot(self, name, folder=None):
return page_actions.save_screenshot(self.driver, name, folder)

def _demo_mode_pause_if_active(self, tiny=False):
if self.demo_mode:
if self.demo_sleep:
wait_time = float(self.demo_sleep)
else:
wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT
if not tiny:
time.sleep(wait_time)
else:
time.sleep(wait_time/3.0)

def _demo_mode_scroll_if_active(self, selector, by):
if self.demo_mode:
if by == By.CSS_SELECTOR:
self.scroll_to(selector)


# PyTest-Specific Code #

def setUp(self):
@@ -243,6 +307,8 @@ def setUp(self):
self.log_path = pytest.config.option.log_path
self.browser = pytest.config.option.browser
self.data = pytest.config.option.data
self.demo_mode = pytest.config.option.demo_mode
self.demo_sleep = pytest.config.option.demo_sleep
if self.with_selenium:
self.driver = browser_launcher.get_driver(self.browser)

11 changes: 11 additions & 0 deletions seleniumbase/plugins/selenium_plugin.py
Original file line number Diff line number Diff line change
@@ -46,6 +46,15 @@ def options(self, parser, env):
default='4444',
help="""Designates the port used by the test.
Default: 4444.""")
parser.add_option('--demo_mode', action="store_true",
dest='demo_mode',
default=False,
help="""Using this slows down the automation so that
you can see what it's actually doing.""")
parser.add_option('--demo_sleep', action='store', dest='demo_sleep',
default=None,
help="""Setting this overrides the Demo Mode sleep
time that happens after browser actions.""")

def configure(self, options, conf):
super(SeleniumBrowser, self).configure(options, conf)
@@ -95,6 +104,8 @@ def beforeTest(self, test):
else:
version = ""
test.test.browser = "%s%s" % (self.options.browser, version)
test.test.demo_mode = self.options.demo_mode
test.test.demo_sleep = self.options.demo_sleep
except Exception as err:
print "Error starting/connecting to Selenium:"
print err
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@

setup(
name='seleniumbase',
version='1.1.15',
version='1.1.16',
url='https://github.com/mdmintz/SeleniumBase',
author='Michael Mintz',
author_email='@mintzworld',