Commit 2cdd005f by chrisndodge

Merge pull request #1037 from edx/rc/2013-09-18

Rc/2013 09 18
parents d3dc9d27 71a5df72
...@@ -5,6 +5,10 @@ These are notable changes in edx-platform. This is a rolling list of changes, ...@@ -5,6 +5,10 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected. the top. Include a label indicating the component affected.
Studio/LMS: Added ability to set due date formatting through Studio's Advanced Settings.
The key is due_date_display_format, and the value should be a format supported by Python's
strftime function.
Common: Added configurable backends for tracking events. Tracking events using Common: Added configurable backends for tracking events. Tracking events using
the python logging module is the default backend. Support for MongoDB and a the python logging module is the default backend. Support for MongoDB and a
Django database is also available. Django database is also available.
......
...@@ -67,7 +67,7 @@ def get_index(name): ...@@ -67,7 +67,7 @@ def get_index(name):
page_name_css = 'section[data-type="HTMLModule"]' page_name_css = 'section[data-type="HTMLModule"]'
all_pages = world.css_find(page_name_css) all_pages = world.css_find(page_name_css)
for i in range(len(all_pages)): for i in range(len(all_pages)):
if all_pages[i].html == '\n {name}\n'.format(name=name): if world.retry_on_exception(lambda: all_pages[i].html) == '\n {name}\n'.format(name=name):
return i return i
return None return None
......
...@@ -6,6 +6,7 @@ import time ...@@ -6,6 +6,7 @@ import time
import platform import platform
from urllib import quote_plus from urllib import quote_plus
from selenium.common.exceptions import WebDriverException, TimeoutException from selenium.common.exceptions import WebDriverException, TimeoutException
from selenium.common.exceptions import StaleElementReferenceException
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
...@@ -45,18 +46,25 @@ def css_has_text(css_selector, text, index=0): ...@@ -45,18 +46,25 @@ def css_has_text(css_selector, text, index=0):
@world.absorb @world.absorb
def wait_for(func, timeout=5): def wait_for(func, timeout=5):
WebDriverWait(world.browser.driver, timeout).until(func) WebDriverWait(
driver=world.browser.driver,
timeout=timeout,
ignored_exceptions=(StaleElementReferenceException)
).until(func)
def wait_for_present(css_selector, timeout=30): def wait_for_present(css_selector, timeout=30):
""" """
Waiting for the element to be present in the DOM. Waiting for the element to be present in the DOM.
Throws an error if the wait_for time expires. Throws an error if the WebDriverWait timeout clock expires.
Otherwise this method will return None Otherwise this method will return None
""" """
try: try:
WebDriverWait(driver=world.browser.driver, WebDriverWait(
timeout=60).until(EC.presence_of_element_located((By.CSS_SELECTOR, css_selector,))) driver=world.browser.driver,
timeout=timeout,
ignored_exceptions=(StaleElementReferenceException)
).until(EC.presence_of_element_located((By.CSS_SELECTOR, css_selector,)))
except TimeoutException: except TimeoutException:
raise TimeoutException("Timed out waiting for {} to be present.".format(css_selector)) raise TimeoutException("Timed out waiting for {} to be present.".format(css_selector))
...@@ -65,12 +73,15 @@ def wait_for_present(css_selector, timeout=30): ...@@ -65,12 +73,15 @@ def wait_for_present(css_selector, timeout=30):
def wait_for_visible(css_selector, timeout=30): def wait_for_visible(css_selector, timeout=30):
""" """
Waiting for the element to be visible in the DOM. Waiting for the element to be visible in the DOM.
Throws an error if the wait_for time expires. Throws an error if the WebDriverWait timeout clock expires.
Otherwise this method will return None Otherwise this method will return None
""" """
try: try:
WebDriverWait(driver=world.browser.driver, WebDriverWait(
timeout=timeout).until(EC.visibility_of_element_located((By.CSS_SELECTOR, css_selector,))) driver=world.browser.driver,
timeout=timeout,
ignored_exceptions=(StaleElementReferenceException)
).until(EC.visibility_of_element_located((By.CSS_SELECTOR, css_selector,)))
except TimeoutException: except TimeoutException:
raise TimeoutException("Timed out waiting for {} to be visible.".format(css_selector)) raise TimeoutException("Timed out waiting for {} to be visible.".format(css_selector))
...@@ -79,12 +90,15 @@ def wait_for_visible(css_selector, timeout=30): ...@@ -79,12 +90,15 @@ def wait_for_visible(css_selector, timeout=30):
def wait_for_invisible(css_selector, timeout=30): def wait_for_invisible(css_selector, timeout=30):
""" """
Waiting for the element to be either invisible or not present on the DOM. Waiting for the element to be either invisible or not present on the DOM.
Throws an error if the wait_for time expires. Throws an error if the WebDriverWait timeout clock expires.
Otherwise this method will return None Otherwise this method will return None
""" """
try: try:
WebDriverWait(driver=world.browser.driver, WebDriverWait(
timeout=timeout).until(EC.invisibility_of_element_located((By.CSS_SELECTOR, css_selector,))) driver=world.browser.driver,
timeout=timeout,
ignored_exceptions=(StaleElementReferenceException)
).until(EC.invisibility_of_element_located((By.CSS_SELECTOR, css_selector,)))
except TimeoutException: except TimeoutException:
raise TimeoutException("Timed out waiting for {} to be invisible.".format(css_selector)) raise TimeoutException("Timed out waiting for {} to be invisible.".format(css_selector))
...@@ -93,14 +107,17 @@ def wait_for_invisible(css_selector, timeout=30): ...@@ -93,14 +107,17 @@ def wait_for_invisible(css_selector, timeout=30):
def wait_for_clickable(css_selector, timeout=30): def wait_for_clickable(css_selector, timeout=30):
""" """
Waiting for the element to be present and clickable. Waiting for the element to be present and clickable.
Throws an error if the wait_for time expires. Throws an error if the WebDriverWait timeout clock expires.
Otherwise this method will return None. Otherwise this method will return None.
""" """
# Sometimes the element is clickable then gets obscured. # Sometimes the element is clickable then gets obscured.
# In this case, pause so that it is not reported clickable too early # In this case, pause so that it is not reported clickable too early
try: try:
WebDriverWait(world.browser.driver, WebDriverWait(
timeout=timeout).until(EC.element_to_be_clickable((By.CSS_SELECTOR, css_selector,))) driver=world.browser.driver,
timeout=timeout,
ignored_exceptions=(StaleElementReferenceException)
).until(EC.element_to_be_clickable((By.CSS_SELECTOR, css_selector,)))
except TimeoutException: except TimeoutException:
raise TimeoutException("Timed out waiting for {} to be clickable.".format(css_selector)) raise TimeoutException("Timed out waiting for {} to be clickable.".format(css_selector))
...@@ -133,7 +150,7 @@ def css_click(css_selector, index=0, wait_time=30): ...@@ -133,7 +150,7 @@ def css_click(css_selector, index=0, wait_time=30):
# another element might be on top of it. In this case, try # another element might be on top of it. In this case, try
# clicking in the upper left corner. # clicking in the upper left corner.
try: try:
return world.css_find(css_selector)[index].click() return retry_on_exception(lambda: world.css_find(css_selector)[index].click())
except WebDriverException: except WebDriverException:
return css_click_at(css_selector, index=index) return css_click_at(css_selector, index=index)
...@@ -177,19 +194,19 @@ def id_click(elem_id): ...@@ -177,19 +194,19 @@ def id_click(elem_id):
@world.absorb @world.absorb
def css_fill(css_selector, text, index=0): def css_fill(css_selector, text, index=0):
css_find(css_selector)[index].fill(text) retry_on_exception(lambda: css_find(css_selector)[index].fill(text))
@world.absorb @world.absorb
def click_link(partial_text, index=0): def click_link(partial_text, index=0):
world.browser.find_link_by_partial_text(partial_text)[index].click() retry_on_exception(lambda: world.browser.find_link_by_partial_text(partial_text)[index].click())
@world.absorb @world.absorb
def css_text(css_selector, index=0, timeout=30): def css_text(css_selector, index=0, timeout=30):
# Wait for the css selector to appear # Wait for the css selector to appear
if is_css_present(css_selector): if is_css_present(css_selector):
return css_find(css_selector, wait_time=timeout)[index].text return retry_on_exception(lambda: css_find(css_selector, wait_time=timeout)[index].text)
else: else:
return "" return ""
...@@ -198,7 +215,7 @@ def css_text(css_selector, index=0, timeout=30): ...@@ -198,7 +215,7 @@ def css_text(css_selector, index=0, timeout=30):
def css_value(css_selector, index=0): def css_value(css_selector, index=0):
# Wait for the css selector to appear # Wait for the css selector to appear
if is_css_present(css_selector): if is_css_present(css_selector):
return css_find(css_selector)[index].value return retry_on_exception(lambda: css_find(css_selector)[index].value)
else: else:
return "" return ""
...@@ -209,18 +226,18 @@ def css_html(css_selector, index=0): ...@@ -209,18 +226,18 @@ def css_html(css_selector, index=0):
Returns the HTML of a css_selector Returns the HTML of a css_selector
""" """
assert is_css_present(css_selector) assert is_css_present(css_selector)
return css_find(css_selector)[index].html return retry_on_exception(lambda: css_find(css_selector)[index].html)
@world.absorb @world.absorb
def css_has_class(css_selector, class_name, index=0): def css_has_class(css_selector, class_name, index=0):
return css_find(css_selector)[index].has_class(class_name) return retry_on_exception(lambda: css_find(css_selector)[index].has_class(class_name))
@world.absorb @world.absorb
def css_visible(css_selector, index=0): def css_visible(css_selector, index=0):
assert is_css_present(css_selector) assert is_css_present(css_selector)
return css_find(css_selector)[index].visible return retry_on_exception(lambda: css_find(css_selector)[index].visible)
@world.absorb @world.absorb
...@@ -277,15 +294,21 @@ def trigger_event(css_selector, event='change', index=0): ...@@ -277,15 +294,21 @@ def trigger_event(css_selector, event='change', index=0):
@world.absorb @world.absorb
def retry_on_exception(func, max_attempts=5): def retry_on_exception(func, max_attempts=5, ignored_exceptions=StaleElementReferenceException):
"""
Retry the interaction, ignoring the passed exceptions.
By default ignore StaleElementReferenceException, which happens often in our application
when the DOM is being manipulated by client side JS.
Note that ignored_exceptions is passed directly to the except block, and as such can be
either a single exception or multiple exceptions as a parenthesized tuple.
"""
attempt = 0 attempt = 0
while attempt < max_attempts: while attempt < max_attempts:
try: try:
return func() return func()
break break
except WebDriverException: except ignored_exceptions:
world.wait(1) world.wait(1)
attempt += 1 attempt += 1
except:
attempt += 1
assert_true(attempt < max_attempts, 'Ran out of attempts to execute {}'.format(func)) assert_true(attempt < max_attempts, 'Ran out of attempts to execute {}'.format(func))
...@@ -227,17 +227,13 @@ class LTIModule(LTIFields, XModule): ...@@ -227,17 +227,13 @@ class LTIModule(LTIFields, XModule):
body=body, body=body,
headers=headers) headers=headers)
except ValueError: # scheme not in url except ValueError: # scheme not in url
# Stubbing headers for now: #https://github.com/idan/oauthlib/blob/master/oauthlib/oauth1/rfc5849/signature.py#L136
#Stubbing headers for now:
headers = { headers = {
u'Content-Type': u'application/x-www-form-urlencoded', u'Content-Type': u'application/x-www-form-urlencoded',
u'Authorization': u'oAuth ' # cont.. u'Authorization': u'OAuth oauth_nonce="80966668944732164491378916897", \
u'oauth_nonce="80966668944732164491378916897", ' # cont.. oauth_timestamp="1378916897", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", \
u'oauth_timestamp="1378916897", ' # cont.. oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
u'oauth_version="1.0", ' # cont..
u'oauth_signature_method="HMAC-SHA1", ' # cont..
u'oauth_consumer_key="", ' # cont..
u'oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"',
}
params = headers['Authorization'] params = headers['Authorization']
# parse headers to pass to template as part of context: # parse headers to pass to template as part of context:
......
...@@ -1661,13 +1661,13 @@ ...@@ -1661,13 +1661,13 @@
margin: 0 flex-gutter() 0 0; margin: 0 flex-gutter() 0 0;
.title { .title {
@extend .hd-lv4; @extend %hd-lv4;
margin-bottom: ($baseline/4); margin-bottom: ($baseline/4);
} }
.copy { .copy {
@extend .t-copy-sub1; @extend %t-copy-sub1;
@extend .t-weight3; @extend %t-weight3;
} }
.list-actions { .list-actions {
...@@ -1675,7 +1675,7 @@ ...@@ -1675,7 +1675,7 @@
} }
.action-verify label { .action-verify label {
@extend .t-copy-sub1; @extend %t-copy-sub1;
} }
} }
......
...@@ -4,13 +4,13 @@ ...@@ -4,13 +4,13 @@
<div class="home"> <div class="home">
<a href="#" class="home-icon"> <a href="#" class="home-icon">
<i class="icon icon-home"></i> <i class="icon icon-home"></i>
<span class="text-sr">${_("Discussion Home")}</span> <span class="sr">${_("Discussion Home")}</span>
</a> </a>
</div> </div>
<div class="browse is-open"> <div class="browse is-open">
<a href="#" class="browse-topic-drop-icon"> <a href="#" class="browse-topic-drop-icon">
<i class="icon icon-reorder"></i> <i class="icon icon-reorder"></i>
<span class="text-sr">${_("Discussion Topics")}</span> <span class="sr">${_("Discussion Topics")}</span>
</a> </a>
<a href="#" class="browse-topic-drop-btn"><span class="current-board">${_("Show All Discussions")}</span> <span class="drop-arrow">▾</span></a> <a href="#" class="browse-topic-drop-btn"><span class="current-board">${_("Show All Discussions")}</span> <span class="drop-arrow">▾</span></a>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment