Skip to content

Commit 9995c39

Browse files
committed
Merge pull request #6 from mdmintz/Demo-Mode
Demo Mode for SeleniumBase
2 parents 3f57b9b + 5396e0d commit 9995c39

File tree

7 files changed

+122
-10
lines changed

7 files changed

+122
-10
lines changed

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,25 @@ nosetests my_first_test.py --browser=firefox --with-selenium -s
199199
```
200200

201201
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.
202-
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:
202+
203+
If the example is moving too fast for your eyes to see what's going on, there are a few things you can do.
204+
You can add ``--demo_mode`` on the command line, which pauses the browser for about a second (by default) after each action:
205+
206+
```bash
207+
nosetests my_first_test.py --with-selenium -s --demo_mode
208+
```
209+
210+
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.)
211+
212+
```bash
213+
nosetests my_first_test.py --with-selenium -s --demo_mode --demo_sleep=1.2
214+
```
215+
216+
You can also add either of the following to your scripts to slow down the tests:
203217

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

209223
(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).)

conftest.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,27 @@ def pytest_addoption(parser):
3434
parser.addoption('--log_path', dest='log_path',
3535
default='logs/',
3636
help='Where the log files are saved.')
37+
parser.addoption('--demo_mode', action="store_true",
38+
dest='demo_mode',
39+
default=False,
40+
help="""Using this slows down the automation so that
41+
you can see what it's actually doing.""")
42+
parser.addoption('--demo_sleep', action='store', dest='demo_sleep',
43+
default=None,
44+
help="""Setting this overrides the Demo Mode sleep
45+
time that happens after browser actions.""")
3746

3847

3948
def pytest_configure(config):
4049
with_selenium = config.getoption('with_selenium')
4150
with_testing_base = config.getoption('with_testing_base')
4251
browser = config.getoption('browser')
4352
log_path = config.getoption('log_path')
53+
demo_mode = config.getoption('demo_mode')
54+
demo_sleep = ''
4455
data = ''
56+
if config.getoption('demo_sleep') is not None:
57+
demo_sleep = config.getoption('demo_sleep')
4558
if config.getoption('data') is not None:
4659
data = config.getoption('data')
4760
# Create a temporary config file while tests are running
@@ -52,6 +65,8 @@ def pytest_configure(config):
5265
config_file.write("data:::%s\n" % data)
5366
config_file.write("with_testing_base:::%s\n" % with_testing_base)
5467
config_file.write("log_path:::%s\n" % log_path)
68+
config_file.write("demo_mode:::%s\n" % demo_mode)
69+
config_file.write("demo_sleep:::%s\n" % demo_sleep)
5570
config_file.close()
5671
log_folder_setup(config)
5772

docker/docker_setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
setup(
1010
name='seleniumbase',
11-
version='1.1.15',
11+
version='1.1.16',
1212
author='Michael Mintz',
1313
author_email='@mintzworld',
1414
maintainer='Michael Mintz',

seleniumbase/config/settings.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
LARGE_TIMEOUT = 10
1111
EXTREME_TIMEOUT = 30
1212

13+
# Default time to wait after each browser action performed during Demo Mode
14+
# Use Demo Mode when you want others to see what your automation is doing
15+
# Usage: --demo_mode when run from the command line when using --with-selenium
16+
# This value can be overwritten on the command line by using --demo_sleep=FLOAT
17+
DEFAULT_DEMO_MODE_TIMEOUT = 1.2
18+
1319
# If True, existing logs from past test runs will be saved and take up space.
1420
# If False, only the logs from the most recent test run will be saved locally.
1521
# This has no effect on Jenkins/S3/MySQL, which may still be saving test logs.

seleniumbase/fixtures/base_case.py

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def open(self, url):
3838
self.driver.get(url)
3939
if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:
4040
self.wait_for_ready_state_complete()
41+
self._demo_mode_pause_if_active()
4142

4243
def open_url(self, url):
4344
""" In case people are mixing up self.open() with open(),
@@ -48,9 +49,11 @@ def click(self, selector, by=By.CSS_SELECTOR,
4849
timeout=settings.SMALL_TIMEOUT):
4950
element = page_actions.wait_for_element_visible(
5051
self.driver, selector, by, timeout=timeout)
52+
self._demo_mode_scroll_if_active(selector, by)
5153
element.click()
5254
if settings.WAIT_FOR_RSC_ON_CLICKS:
5355
self.wait_for_ready_state_complete()
56+
self._demo_mode_pause_if_active()
5457

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

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

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

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

125132
def set_window_size(self, width, height):
126133
return self.driver.set_window_size(width, height)
134+
self._demo_mode_pause_if_active()
127135

128136
def maximize_window(self):
129137
return self.driver.maximize_window()
138+
self._demo_mode_pause_if_active()
130139

131140
def activate_jquery(self):
132-
""" (It's not on by default on all website pages.) """
141+
""" If "jQuery is not defined", use this method to activate it for use.
142+
This happens because jQuery is not always defined on web sites. """
143+
try:
144+
# Let's first find out if jQuery is already defined.
145+
self.driver.execute_script("jQuery('html')")
146+
# Since that command worked, jQuery is defined. Let's return.
147+
return
148+
except Exception:
149+
# jQuery is not currently defined. Let's proceed by defining it.
150+
pass
133151
self.driver.execute_script(
134152
'''var script = document.createElement("script"); '''
135153
'''script.src = "https://ajax.googleapis.com/ajax/libs/jquery/1/'''
136154
'''jquery.min.js"; document.getElementsByTagName("head")[0]'''
137155
'''.appendChild(script);''')
156+
for x in xrange(30):
157+
# jQuery needs a small amount of time to activate. (At most 3s)
158+
try:
159+
self.driver.execute_script("jQuery('html')")
160+
return
161+
except Exception:
162+
time.sleep(0.1)
163+
# Since jQuery still isn't activating, give up and raise an exception
164+
raise Exception("Exception: WebDriver could not activate jQuery!")
138165

139166
def scroll_to(self, selector):
140167
self.wait_for_element_visible(selector, timeout=settings.SMALL_TIMEOUT)
141-
self.driver.execute_script(
142-
"jQuery('%s')[0].scrollIntoView()" % selector)
168+
scroll_script = "jQuery('%s')[0].scrollIntoView()" % selector
169+
try:
170+
self.driver.execute_script(scroll_script)
171+
except Exception:
172+
# The likely reason this fails is because: "jQuery is not defined"
173+
self.activate_jquery() # It's a good thing we can define it here
174+
self.driver.execute_script(scroll_script)
175+
self._demo_mode_pause_if_active(tiny=True)
143176

144177
def scroll_click(self, selector):
145178
self.scroll_to(selector)
146179
self.click(selector)
147180

148181
def jquery_click(self, selector):
182+
self.scroll_to(selector)
149183
self.driver.execute_script("jQuery('%s').click()" % selector)
184+
self._demo_mode_pause_if_active()
150185

151186
def jq_format(self, code):
152187
return page_utils.jq_format(code)
153188

154189
def set_value(self, selector, value):
190+
self.scroll_to(selector)
155191
val = json.dumps(value)
156192
self.driver.execute_script("jQuery('%s').val(%s)" % (selector, val))
193+
self._demo_mode_pause_if_active()
157194

158195
def jquery_update_text_value(self, selector, new_value,
159196
timeout=settings.SMALL_TIMEOUT):
160197
element = self.wait_for_element_visible(selector, timeout=timeout)
198+
self.scroll_to(selector)
161199
self.driver.execute_script("""jQuery('%s').val('%s')"""
162200
% (selector, self.jq_format(new_value)))
163201
if new_value.endswith('\n'):
164202
element.send_keys('\n')
203+
self._demo_mode_pause_if_active()
165204

166205
def jquery_update_text(self, selector, new_value,
167206
timeout=settings.SMALL_TIMEOUT):
168207
self.jquery_update_text_value(selector, new_value, timeout=timeout)
169208

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

173215
def hover_and_click(self, hover_selector, click_selector,
174216
click_by=By.CSS_SELECTOR,
175217
timeout=settings.SMALL_TIMEOUT):
176-
return page_actions.hover_and_click(self.driver, hover_selector,
177-
click_selector, click_by, timeout)
218+
self.wait_for_element_visible(hover_selector, timeout=timeout)
219+
self.scroll_to(hover_selector)
220+
# Settle down from the scrolling before hovering
221+
element = page_actions.hover_and_click(
222+
self.driver, hover_selector, click_selector, click_by, timeout)
223+
self._demo_mode_pause_if_active()
224+
return element
178225

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

271+
def _demo_mode_pause_if_active(self, tiny=False):
272+
if self.demo_mode:
273+
if self.demo_sleep:
274+
wait_time = float(self.demo_sleep)
275+
else:
276+
wait_time = settings.DEFAULT_DEMO_MODE_TIMEOUT
277+
if not tiny:
278+
time.sleep(wait_time)
279+
else:
280+
time.sleep(wait_time/3.0)
281+
282+
def _demo_mode_scroll_if_active(self, selector, by):
283+
if self.demo_mode:
284+
if by == By.CSS_SELECTOR:
285+
self.scroll_to(selector)
286+
287+
224288
# PyTest-Specific Code #
225289

226290
def setUp(self):
@@ -243,6 +307,8 @@ def setUp(self):
243307
self.log_path = pytest.config.option.log_path
244308
self.browser = pytest.config.option.browser
245309
self.data = pytest.config.option.data
310+
self.demo_mode = pytest.config.option.demo_mode
311+
self.demo_sleep = pytest.config.option.demo_sleep
246312
if self.with_selenium:
247313
self.driver = browser_launcher.get_driver(self.browser)
248314

seleniumbase/plugins/selenium_plugin.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ def options(self, parser, env):
4646
default='4444',
4747
help="""Designates the port used by the test.
4848
Default: 4444.""")
49+
parser.add_option('--demo_mode', action="store_true",
50+
dest='demo_mode',
51+
default=False,
52+
help="""Using this slows down the automation so that
53+
you can see what it's actually doing.""")
54+
parser.add_option('--demo_sleep', action='store', dest='demo_sleep',
55+
default=None,
56+
help="""Setting this overrides the Demo Mode sleep
57+
time that happens after browser actions.""")
4958

5059
def configure(self, options, conf):
5160
super(SeleniumBrowser, self).configure(options, conf)
@@ -95,6 +104,8 @@ def beforeTest(self, test):
95104
else:
96105
version = ""
97106
test.test.browser = "%s%s" % (self.options.browser, version)
107+
test.test.demo_mode = self.options.demo_mode
108+
test.test.demo_sleep = self.options.demo_sleep
98109
except Exception as err:
99110
print "Error starting/connecting to Selenium:"
100111
print err

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
setup(
88
name='seleniumbase',
9-
version='1.1.15',
9+
version='1.1.16',
1010
url='https://github.com/mdmintz/SeleniumBase',
1111
author='Michael Mintz',
1212
author_email='@mintzworld',

0 commit comments

Comments
 (0)